@hestia-earth/ui-components 0.42.5 → 0.42.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ import { NgbTooltip, NgbDropdown, NgbDropdownMenu, NgbDropdownToggle, NgbActiveM
9
9
  import { ReplaySubject, of, mergeMap, shareReplay, delay, map, catchError, distinctUntilChanged, timer, take, first, Subject, combineLatest, filter, fromEvent, startWith, merge, skip, switchMap, EMPTY, throttleTime, animationFrameScheduler, debounceTime, takeUntil, lastValueFrom, tap, skipUntil, zip, mergeAll, reduce, forkJoin, throwError, from, firstValueFrom, toArray, distinct, groupBy } from 'rxjs';
10
10
  import { HttpClient } from '@angular/common/http';
11
11
  import get from 'lodash.get';
12
- import { SCHEMA_VERSION, SchemaType, NodeType, TermTermType, productTermTermType, nestedSearchableKeys, SiteSiteType, EmissionMethodTier, isExpandable, sortKeysByType, isTypeNode, BlankNodesKey, impactAssessmentTermTermType, measurementTermTermType, emissionTermTermType, inputTermTermType, CycleFunctionalUnit, NonBlankNodesKey, jsonldPath, isTypeValid, isTypeBlankNode, typeToSchemaType, infrastructureTermTermType, managementTermTermType } from '@hestia-earth/schema';
12
+ import { SCHEMA_VERSION, SchemaType, NodeType, TermTermType, productTermTermType, nestedSearchableKeys, SiteSiteType, EmissionMethodTier, isExpandable, sortKeysByType, isTypeNode, uniquenessFields, BlankNodesKey, impactAssessmentTermTermType, measurementTermTermType, emissionTermTermType, inputTermTermType, CycleFunctionalUnit, NonBlankNodesKey, jsonldPath, isTypeValid, isTypeBlankNode, typeToSchemaType, infrastructureTermTermType, managementTermTermType } from '@hestia-earth/schema';
13
13
  import Gradient from 'javascript-color-gradient';
14
14
  import { ShadeGenerator } from 'shade-generator/dist/shadeGenerator';
15
15
  import { select, selectAll } from 'd3-selection';
@@ -7428,7 +7428,7 @@ class HeNodeService {
7428
7428
  params
7429
7429
  })
7430
7430
  .pipe(catchError(() => of('')))
7431
- : this.http.get(url, { params }).pipe(catchError(() => of({})));
7431
+ : this.http.get(url, { params: { ...params, format: 'json' } }).pipe(catchError(() => of({})));
7432
7432
  }
7433
7433
  getContributions$({ dataState, ...impactAssessment }) {
7434
7434
  return this.http.get(`${this.nodeUrl(impactAssessment)}/contributions`).pipe(mergeMap(contributions => isEmpty(contributions) ? this.getContributionsFromLog$(impactAssessment) : of(contributions)), catchError(() => this.getContributionsFromLog$(impactAssessment)));
@@ -8055,8 +8055,8 @@ const inputGroupsTermTypes = [TermTermType.pesticideBrandName, TermTermType.fert
8055
8055
  const defaultGroupOpen = false;
8056
8056
  const noValue = 'None';
8057
8057
  // add any other models that are set as "not relevant"
8058
- const notRelevantModels = ['emissionNotRelevant'];
8059
- const isNotRelevantModel = (model) => notRelevantModels.includes(model.methodId);
8058
+ const notRelevantModels$1 = ['emissionNotRelevant'];
8059
+ const isNotRelevantModel$1 = (model) => notRelevantModels$1.includes(model.methodId);
8060
8060
  const asArray = (values) => (Array.isArray(values) ? values : []);
8061
8061
  const subValueKeys = ['transformation', 'animal'];
8062
8062
  const computeTerms = (originalValues, recalculatedValues, terms, filterTermTypes, extraTerms = []) => orderBy(uniqBy([
@@ -8079,7 +8079,7 @@ var LogStatus;
8079
8079
  })(LogStatus || (LogStatus = {}));
8080
8080
  const hasLogs = (logs) => !!logs?.requirements || !!logs?.logs || logs?.missingLookups?.length > 0;
8081
8081
  const hasLogDetails = (status, logs) => [LogStatus.success, LogStatus.error, LogStatus.dataProvided].includes(status) && hasLogs(logs);
8082
- const isRecalculatedByModel = (data, model) => 'recalculated' in data && data.recalculated?.some(value => blankNodeModelId(value) === model.methodId);
8082
+ const isRecalculatedByModel = (data, model) => 'recalculated' in data && data.recalculated?.some(value => blankNodeModelId$1(value) === model.methodId);
8083
8083
  const logStatus = (data, logs, model, hasPreviousSuccess = false) => {
8084
8084
  const termLogs = logs?.[model.methodId];
8085
8085
  const withLogs = hasLog(termLogs);
@@ -8090,7 +8090,7 @@ const logStatus = (data, logs, model, hasPreviousSuccess = false) => {
8090
8090
  ].every(Boolean);
8091
8091
  // can be not required for a specific model
8092
8092
  const isRequired = !!termLogs && 'runRequired' in termLogs ? termLogs.runRequired : data.isRequired;
8093
- const isCurrentModel = (data.recalculated ?? []).some(v => blankNodeModelId(v) == model.methodId);
8093
+ const isCurrentModel = (data.recalculated ?? []).some(v => blankNodeModelId$1(v) == model.methodId);
8094
8094
  return withLogs
8095
8095
  ? !isRequired
8096
8096
  ? LogStatus.notRequired
@@ -8135,7 +8135,7 @@ const mergeSubValues = (values) => Object.values(values.reduce((prev, { id, key,
8135
8135
  };
8136
8136
  return prev;
8137
8137
  }, {}));
8138
- const reduceValues = (values, termId) => {
8138
+ const reduceValues$1 = (values, termId) => {
8139
8139
  const propertyValues = values
8140
8140
  .map(({ value }) => propertyValue$1(value, termId))
8141
8141
  // propertyValue may return null if the value is null or undefined, therefore remove them from total
@@ -8170,11 +8170,11 @@ const groupSubvalues = (subValues, termsGrouping) => Object.keys(termsGrouping).
8170
8170
  const blankNodeValueByKey = {
8171
8171
  property: (values, _k, id) => !!id && values?.length ? (values[0].properties || []).find(p => p.term['@id'] === id)?.value || '' : '',
8172
8172
  input: (values, _k, id, animalId) => !!id && (values || []).filter(v => v['@type'] === SchemaType.Emission || v['@type'] === SchemaType.Indicator).length
8173
- ? reduceValues(values.filter(({ inputs, animals }) => (inputs || []).some(input => input['@id'] === id) &&
8173
+ ? reduceValues$1(values.filter(({ inputs, animals }) => (inputs || []).some(input => input['@id'] === id) &&
8174
8174
  (!animalId || (animals || []).some(animal => animal['@id'] === animalId))), id)
8175
8175
  : '',
8176
8176
  transformation: (values, _k, id) => values.length
8177
- ? reduceValues(values.filter(({ transformation }) => transformation?.['@id'] === id), id)
8177
+ ? reduceValues$1(values.filter(({ transformation }) => transformation?.['@id'] === id), id)
8178
8178
  : '',
8179
8179
  default: (blankNodes, key) => {
8180
8180
  const value = blankNodes?.length ? blankNodes[0][key] : null;
@@ -8267,9 +8267,9 @@ const logAnimals = (node) => (node.animals ?? []).map(animal => ({
8267
8267
  }));
8268
8268
  const includeCycleSubValue = (subValues) => subValues.some(v => !['property', 'backgroundData'].includes(v.key));
8269
8269
  const cycleSubValue = ({ termId, configModels, isRequired, subValues, original, recalculated }) => {
8270
- const originalValue = reduceValues(original.filter((v) => !v.inputs?.length && !v.transformation), termId);
8270
+ const originalValue = reduceValues$1(original.filter((v) => !v.inputs?.length && !v.transformation), termId);
8271
8271
  const recalculatedValues = recalculated.filter((v) => !v.inputs?.length && !v.transformation);
8272
- const recalculatedValue = reduceValues(recalculatedValues, termId);
8272
+ const recalculatedValue = reduceValues$1(recalculatedValues, termId);
8273
8273
  return includeCycleSubValue(subValues) && !isEmpty(recalculatedValue)
8274
8274
  ? {
8275
8275
  blankNode: true,
@@ -8454,7 +8454,7 @@ const configModelWithDocs = ({ nodeType, type, termId, termType, key }, subValue
8454
8454
  ? findMatchingModel({ model: methodId, modelKey: 'seed_emissions' })
8455
8455
  : undefined) ||
8456
8456
  (modelKey ? findMatchingModel({ modelKey, model: methodId }) : undefined) ||
8457
- (isNotRelevantModel({ methodId }) ? findMatchingModel({ model: methodId }) : undefined);
8457
+ (isNotRelevantModel$1({ methodId }) ? findMatchingModel({ model: methodId }) : undefined);
8458
8458
  return { methodId, model };
8459
8459
  };
8460
8460
  const configModelWithLogs = (data, logs, previousModelSuccess = false) => (model) => {
@@ -8498,7 +8498,7 @@ const dataWithConfigModelLogs = (logs, termId) => (data) => {
8498
8498
  }
8499
8499
  return {
8500
8500
  ...data,
8501
- configModels: filterConfigModels(configModels, (model) => ![LogStatus.notRequired].includes(model?.status) || isNotRelevantModel(model))
8501
+ configModels: filterConfigModels(configModels, (model) => ![LogStatus.notRequired].includes(model?.status) || isNotRelevantModel$1(model))
8502
8502
  };
8503
8503
  };
8504
8504
  /**
@@ -8526,8 +8526,8 @@ const groupParallelModels = (config, termId, modelKey, models) => {
8526
8526
  .map(v => (v.length === 1 ? v[0] : v));
8527
8527
  };
8528
8528
  const allParallel = (models) => models.length === 1 && Array.isArray(models[0]) && models[0].length > 1;
8529
- const blankNodeModelId = (value) => (value.methodModel || value.method || value.model)?.['@id'];
8530
- const valueByMethodId = (values, methodId, termId) => propertyValue$1(values.find(v => blankNodeModelId(v) === methodId)?.value, termId);
8529
+ const blankNodeModelId$1 = (value) => (value.methodModel || value.method || value.model)?.['@id'];
8530
+ const valueByMethodId = (values, methodId, termId) => propertyValue$1(values.find(v => blankNodeModelId$1(v) === methodId)?.value, termId);
8531
8531
  const groupParallelValues = (values, models, termId) => Array.isArray(models)
8532
8532
  ? Object.fromEntries(models.map(model => [model.methodId, valueByMethodId(values, model.methodId, termId)]))
8533
8533
  : {};
@@ -8558,7 +8558,7 @@ const groupLogsByTerm = (node, logs, config, allOriginalValues, allRecalculatedV
8558
8558
  const modelsFromRecalculated = recalculated
8559
8559
  // ignore background emissions as no models run directly on them
8560
8560
  .filter((v) => ![EmissionMethodTier.background].includes(v.methodTier))
8561
- .map(blankNodeModelId)
8561
+ .map(blankNodeModelId$1)
8562
8562
  .filter(Boolean);
8563
8563
  const models = unique([
8564
8564
  ...modelsFromLogs,
@@ -8577,9 +8577,9 @@ const groupLogsByTerm = (node, logs, config, allOriginalValues, allRecalculatedV
8577
8577
  subValues.some(v => v.key === 'transformation')
8578
8578
  ].every(Boolean);
8579
8579
  const filteredOriginalValues = ignoreTransformationValues ? filterTransformation(original) : original;
8580
- const originalValue = reduceValues(filteredOriginalValues, termId);
8580
+ const originalValue = reduceValues$1(filteredOriginalValues, termId);
8581
8581
  const filteredRecalculatedValues = ignoreTransformationValues ? filterTransformation(recalculated) : recalculated;
8582
- const recalculatedValue = reduceValues(filteredRecalculatedValues, termId);
8582
+ const recalculatedValue = reduceValues$1(filteredRecalculatedValues, termId);
8583
8583
  const nodeLog = dataWithConfigModelLogs(termLogs)({
8584
8584
  blankNode: true,
8585
8585
  isOpen: defaultGroupOpen,
@@ -8691,7 +8691,7 @@ const modelCount = (blankNodeLogs) => Math.max.apply(Math.max, [
8691
8691
  ])
8692
8692
  ]);
8693
8693
  const parseLogValue = (value) => (isNumber(value) ? parseFloat(value) : value);
8694
- const logValueArray = (value) => [';', ':'].some(v => value.includes(v))
8694
+ const logValueArray = (value) => typeof value === 'string' && [';', ':'].some(v => value.includes(v))
8695
8695
  ? value.split(';').flatMap(v => {
8696
8696
  const res = v
8697
8697
  .split('_')
@@ -8778,22 +8778,32 @@ class NodeLogsModelsLogsComponent {
8778
8778
  this.requirements = computed(() => this.logs()?.requirements ?? {}, ...(ngDevMode ? [{ debugName: "requirements" }] : []));
8779
8779
  this.requirementKeys = computed(() => requirementKeys(this.requirements()), ...(ngDevMode ? [{ debugName: "requirementKeys" }] : []));
8780
8780
  this.missingLookups = computed(() => this.logs().missingLookups ?? [], ...(ngDevMode ? [{ debugName: "missingLookups" }] : []));
8781
+ // the untouched `.jlog` entry (new log format only), shown on demand as raw JSON
8782
+ this.raw = computed(() => this.logs()?.raw, ...(ngDevMode ? [{ debugName: "raw" }] : []));
8783
+ this.rawJson = computed(() => {
8784
+ const raw = this.raw();
8785
+ return raw ? JSON.stringify(raw, null, 2) : '';
8786
+ }, ...(ngDevMode ? [{ debugName: "rawJson" }] : []));
8787
+ this.showRaw = signal(false, ...(ngDevMode ? [{ debugName: "showRaw" }] : []));
8781
8788
  this.allLogs = computed(() => [
8782
8789
  ...requirementKeys(this.requirements()).map(key => parseLog$1(key, this.requirements()[key])),
8783
8790
  ...parseLogs(this.logs()?.logs ?? {})
8784
8791
  ].filter(v => v.value !== null), ...(ngDevMode ? [{ debugName: "allLogs" }] : []));
8785
8792
  }
8793
+ toggleRaw() {
8794
+ this.showRaw.update(value => !value);
8795
+ }
8786
8796
  toggleSort(sortBy, sorting) {
8787
8797
  const sortOrder = sorting.sortOrder === 'asc' ? 'desc' : 'asc';
8788
8798
  sorting.sortBy = sortBy;
8789
8799
  sorting.sortOrder = sortOrder;
8790
8800
  }
8791
8801
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeLogsModelsLogsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8792
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeLogsModelsLogsComponent, isStandalone: true, selector: "he-node-logs-models-logs", inputs: { logs: { classPropertyName: "logs", publicName: "logs", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"table-container\">\n <table class=\"table is-dark is-fullwidth\">\n <tbody>\n @for (log of allLogs(); track log.key) {\n <ng-container *ngTemplateOutlet=\"logRow; context: log\" />\n }\n </tbody>\n </table>\n</div>\n\n@if (missingLookups().length) {\n <div class=\"it-mt-2\">\n <p>Data missing (might be optional):</p>\n <div class=\"table-container data-table-container\">\n <table class=\"table is-dark is-fullwidth is-striped\">\n <thead>\n <tr>\n <th class=\"has-text-white\">\n <span>Filename</span>\n </th>\n <th class=\"has-text-white\">\n <span>Column Title</span>\n </th>\n <th class=\"has-text-white\">\n <span>Row (term.id)</span>\n </th>\n </tr>\n </thead>\n <tbody class=\"has-text-white\">\n @for (data of missingLookups(); track dataIndex; let dataIndex = $index) {\n <tr>\n <td>{{ data.filename }}</td>\n <td>{{ data.column }}</td>\n <td>{{ data.termId }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n}\n\n<ng-template #sortColumn let-column=\"column\" let-sorting=\"sorting\">\n <a class=\"is-p-1 has-text-white\" (click)=\"toggleSort(column, sorting)\">\n @if (sorting.sortBy === column) {\n @if (sorting.sortOrder === 'asc') {\n <he-svg-icon name=\"sort-caret-up-filled\" size=\"16\" />\n } @else {\n <he-svg-icon name=\"sort-caret-down-filled\" size=\"16\" />\n }\n } @else {\n <he-svg-icon name=\"sort-caret-stacked-filled-stacked\" size=\"16\" />\n }\n </a>\n</ng-template>\n\n<ng-template #logLineArray let-value=\"value\" let-sorting=\"sorting\">\n <table class=\"table is-dark is-bordered is-striped\">\n @let headers = listHeaders(value);\n @if (headers.length) {\n <thead>\n <tr>\n @for (header of headers; track header) {\n <th class=\"has-text-white\">\n <div class=\"is-nowrap\">\n {{ header }}\n <ng-container *ngTemplateOutlet=\"sortColumn; context: { column: header, sorting }\" />\n </div>\n </th>\n }\n </tr>\n </thead>\n }\n <tbody>\n @for (v of value | sortBy: sorting.sortBy : sorting.sortOrder; track vIndex; let vIndex = $index) {\n <tr>\n @if (headers.length) {\n @for (header of headers; track header) {\n <td>{{ v[header] }}</td>\n }\n } @else {\n <td>{{ v }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n</ng-template>\n\n<ng-template #logLineValue let-key=\"key\" let-value=\"value\">\n @if (requirementLinkedNode(key, value); as linkedNode) {\n <he-node-link\n class=\"is-inline-block is-align-middle\"\n linkClass=\"is-dark\"\n [node]=\"linkedNode\"\n [showExternalLink]=\"true\" />\n } @else {\n <span class=\"is-inline-block is-align-middle\">{{ value }}</span>\n }\n</ng-template>\n\n<ng-template #logRow let-key=\"key\" let-value=\"value\" let-isCompleteness=\"isCompleteness\" let-complete=\"complete\">\n <tr>\n <td class=\"has-border-right | copy-log\">\n <span class=\"is-inline-block is-align-middle\">\n @if (isCompleteness) {\n <span>Data completeness for</span>\n <code class=\"is-mx-1\">{{ key }}</code>\n <span>must be</span>\n <code class=\"is-mx-1\">{{ complete }}</code>\n } @else {\n @switch (key) {\n @default {\n {{ parseKey(key) }}\n }\n @case ('property_id') {\n Property Term &#64;id\n }\n @case ('product_id') {\n Product Term &#64;id\n }\n @case ('crop_product_id') {\n Crop Product Term &#64;id\n }\n @case ('input_id') {\n Input Term &#64;id\n }\n @case ('node_type_allowed') {\n <span>Is the current</span>\n <code class=\"is-mx-1\">Node</code>\n <span>allowed to run this model</span>\n }\n @case ('siteType_allowed') {\n <span>Is the current</span>\n <code class=\"is-mx-1\">siteType</code>\n <span>allowed to run this model</span>\n }\n @case ('product_id_allowed') {\n <span>Are any of the Product</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('site_measurement_id_allowed') {\n <span>Are any of the Measurement</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('product_termType_allowed') {\n <span>Are any of the Product</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n @case ('input_id_allowed') {\n <span>Are any of the Input</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('input_termType_allowed') {\n <span>Are any of the Input</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n <!-- Deprecated -->\n @case ('primary_product_id_allowed') {\n <span>Is the current primary product</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('primary_product_termType_allowed') {\n <span>Is the current primary product</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n @case ('has_pesticides_inputs') {\n <span>Cycle contains</span>\n <code class=\"is-mx-1\">pesticideAI</code>\n <span>Inputs</span>\n }\n @case ('all_pesticideAI_have_lookup_value') {\n <span>All</span>\n <code class=\"is-mx-1\">pesticideAI</code>\n <span>Inputs have a lookup value</span>\n }\n @case ('has_crop_residue_burnt') {\n <code class=\"is-mr-1\">aboveGroundCropResidueBurnt</code>\n <span>is present as Product</span>\n }\n @case ('no_cycle_inputs_feed') {\n <span>\n Cycle has\n <b>no</b>\n Input with\n </span>\n <code>isAnimalFeed=true</code>\n }\n @case ('has_cycle_inputs_feed') {\n <span>\n Cycle has\n <b>some</b>\n Input with\n </span>\n <code>isAnimalFeed=true</code>\n }\n }\n }\n </span>\n <he-clipboard\n class=\"is-inline-block is-align-middle\"\n clipboardClass=\"is-size-7 is-p-1\"\n [value]=\"key\"\n [hideText]=\"true\" />\n </td>\n <td>\n @if (value | isArray) {\n <ng-container *ngTemplateOutlet=\"logLineArray; context: { value: value, sorting: {} }\" />\n } @else {\n <ng-container *ngTemplateOutlet=\"logLineValue; context: { key, value }\" />\n }\n </td>\n </tr>\n</ng-template>\n", styles: [".copy-log he-clipboard{visibility:hidden}.copy-log:hover he-clipboard{visibility:visible}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: ClipboardComponent, selector: "he-clipboard", inputs: ["icon", "value", "disabled", "hideText", "hideIcon", "size", "clipboardClass", "tooltipPlacement"] }, { kind: "pipe", type: SortByPipe, name: "sortBy" }, { kind: "pipe", type: IsArrayPipe, name: "isArray" }] }); }
8802
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeLogsModelsLogsComponent, isStandalone: true, selector: "he-node-logs-models-logs", inputs: { logs: { classPropertyName: "logs", publicName: "logs", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"table-container\">\n <table class=\"table is-dark is-fullwidth\">\n <tbody>\n @for (log of allLogs(); track log.key) {\n <ng-container *ngTemplateOutlet=\"logRow; context: log\" />\n }\n </tbody>\n </table>\n</div>\n\n@if (missingLookups().length) {\n <div class=\"it-mt-2\">\n <p>Data missing (might be optional):</p>\n <div class=\"table-container data-table-container\">\n <table class=\"table is-dark is-fullwidth is-striped\">\n <thead>\n <tr>\n <th class=\"has-text-white\">\n <span>Filename</span>\n </th>\n <th class=\"has-text-white\">\n <span>Column Title</span>\n </th>\n <th class=\"has-text-white\">\n <span>Row (term.id)</span>\n </th>\n </tr>\n </thead>\n <tbody class=\"has-text-white\">\n @for (data of missingLookups(); track dataIndex; let dataIndex = $index) {\n <tr>\n <td>{{ data.filename }}</td>\n <td>{{ data.column }}</td>\n <td>{{ data.termId }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n}\n\n@if (raw()) {\n <div class=\"is-mt-2\">\n <div class=\"is-flex is-align-items-center is-gap-8\">\n <a class=\"has-text-white\" (click)=\"toggleRaw()\">\n <he-svg-icon [name]=\"showRaw() ? 'chevron-down' : 'chevron-right'\" size=\"16\" />\n <span>Raw log</span>\n </a>\n @if (showRaw()) {\n <he-clipboard clipboardClass=\"is-size-7 is-p-1\" [value]=\"rawJson()\" [hideText]=\"true\" />\n }\n </div>\n @if (showRaw()) {\n <pre class=\"raw-log is-mt-1\">{{ rawJson() }}</pre>\n }\n </div>\n}\n\n<ng-template #sortColumn let-column=\"column\" let-sorting=\"sorting\">\n <a class=\"is-p-1 has-text-white\" (click)=\"toggleSort(column, sorting)\">\n @if (sorting.sortBy === column) {\n @if (sorting.sortOrder === 'asc') {\n <he-svg-icon name=\"sort-caret-up-filled\" size=\"16\" />\n } @else {\n <he-svg-icon name=\"sort-caret-down-filled\" size=\"16\" />\n }\n } @else {\n <he-svg-icon name=\"sort-caret-stacked-filled-stacked\" size=\"16\" />\n }\n </a>\n</ng-template>\n\n<ng-template #logLineArray let-value=\"value\" let-sorting=\"sorting\">\n <table class=\"table is-dark is-bordered is-striped\">\n @let headers = listHeaders(value);\n @if (headers.length) {\n <thead>\n <tr>\n @for (header of headers; track header) {\n <th class=\"has-text-white\">\n <div class=\"is-nowrap\">\n {{ header }}\n <ng-container *ngTemplateOutlet=\"sortColumn; context: { column: header, sorting }\" />\n </div>\n </th>\n }\n </tr>\n </thead>\n }\n <tbody>\n @for (v of value | sortBy: sorting.sortBy : sorting.sortOrder; track vIndex; let vIndex = $index) {\n <tr>\n @if (headers.length) {\n @for (header of headers; track header) {\n <td>{{ v[header] }}</td>\n }\n } @else {\n <td>{{ v }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n</ng-template>\n\n<ng-template #logLineValue let-key=\"key\" let-value=\"value\">\n @if (requirementLinkedNode(key, value); as linkedNode) {\n <he-node-link\n class=\"is-inline-block is-align-middle\"\n linkClass=\"is-dark\"\n [node]=\"linkedNode\"\n [showExternalLink]=\"true\" />\n } @else {\n <span class=\"is-inline-block is-align-middle\">{{ value }}</span>\n }\n</ng-template>\n\n<ng-template #logRow let-key=\"key\" let-value=\"value\" let-isCompleteness=\"isCompleteness\" let-complete=\"complete\">\n <tr>\n <td class=\"has-border-right | copy-log\">\n <span class=\"is-inline-block is-align-middle\">\n @if (isCompleteness) {\n <span>Data completeness for</span>\n <code class=\"is-mx-1\">{{ key }}</code>\n <span>must be</span>\n <code class=\"is-mx-1\">{{ complete }}</code>\n } @else {\n @switch (key) {\n @default {\n {{ parseKey(key) }}\n }\n @case ('property_id') {\n Property Term &#64;id\n }\n @case ('product_id') {\n Product Term &#64;id\n }\n @case ('crop_product_id') {\n Crop Product Term &#64;id\n }\n @case ('input_id') {\n Input Term &#64;id\n }\n @case ('node_type_allowed') {\n <span>Is the current</span>\n <code class=\"is-mx-1\">Node</code>\n <span>allowed to run this model</span>\n }\n @case ('siteType_allowed') {\n <span>Is the current</span>\n <code class=\"is-mx-1\">siteType</code>\n <span>allowed to run this model</span>\n }\n @case ('product_id_allowed') {\n <span>Are any of the Product</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('site_measurement_id_allowed') {\n <span>Are any of the Measurement</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('product_termType_allowed') {\n <span>Are any of the Product</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n @case ('input_id_allowed') {\n <span>Are any of the Input</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('input_termType_allowed') {\n <span>Are any of the Input</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n <!-- Deprecated -->\n @case ('primary_product_id_allowed') {\n <span>Is the current primary product</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('primary_product_termType_allowed') {\n <span>Is the current primary product</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n @case ('has_pesticides_inputs') {\n <span>Cycle contains</span>\n <code class=\"is-mx-1\">pesticideAI</code>\n <span>Inputs</span>\n }\n @case ('all_pesticideAI_have_lookup_value') {\n <span>All</span>\n <code class=\"is-mx-1\">pesticideAI</code>\n <span>Inputs have a lookup value</span>\n }\n @case ('has_crop_residue_burnt') {\n <code class=\"is-mr-1\">aboveGroundCropResidueBurnt</code>\n <span>is present as Product</span>\n }\n @case ('no_cycle_inputs_feed') {\n <span>\n Cycle has\n <b>no</b>\n Input with\n </span>\n <code>isAnimalFeed=true</code>\n }\n @case ('has_cycle_inputs_feed') {\n <span>\n Cycle has\n <b>some</b>\n Input with\n </span>\n <code>isAnimalFeed=true</code>\n }\n }\n }\n </span>\n <he-clipboard\n class=\"is-inline-block is-align-middle\"\n clipboardClass=\"is-size-7 is-p-1\"\n [value]=\"key\"\n [hideText]=\"true\" />\n </td>\n <td>\n @if (value | isArray) {\n <ng-container *ngTemplateOutlet=\"logLineArray; context: { value: value, sorting: {} }\" />\n } @else {\n <ng-container *ngTemplateOutlet=\"logLineValue; context: { key, value }\" />\n }\n </td>\n </tr>\n</ng-template>\n", styles: [".copy-log he-clipboard{visibility:hidden}.copy-log:hover he-clipboard{visibility:visible}.raw-log{max-height:300px;overflow:auto;white-space:pre;font-size:.75rem;background:#00000059;color:inherit;padding:.5rem;border-radius:3px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: ClipboardComponent, selector: "he-clipboard", inputs: ["icon", "value", "disabled", "hideText", "hideIcon", "size", "clipboardClass", "tooltipPlacement"] }, { kind: "pipe", type: SortByPipe, name: "sortBy" }, { kind: "pipe", type: IsArrayPipe, name: "isArray" }] }); }
8793
8803
  }
8794
8804
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeLogsModelsLogsComponent, decorators: [{
8795
8805
  type: Component$1,
8796
- args: [{ selector: 'he-node-logs-models-logs', imports: [NgTemplateOutlet, HESvgIconComponent, NodeLinkComponent, ClipboardComponent, SortByPipe, IsArrayPipe], template: "<div class=\"table-container\">\n <table class=\"table is-dark is-fullwidth\">\n <tbody>\n @for (log of allLogs(); track log.key) {\n <ng-container *ngTemplateOutlet=\"logRow; context: log\" />\n }\n </tbody>\n </table>\n</div>\n\n@if (missingLookups().length) {\n <div class=\"it-mt-2\">\n <p>Data missing (might be optional):</p>\n <div class=\"table-container data-table-container\">\n <table class=\"table is-dark is-fullwidth is-striped\">\n <thead>\n <tr>\n <th class=\"has-text-white\">\n <span>Filename</span>\n </th>\n <th class=\"has-text-white\">\n <span>Column Title</span>\n </th>\n <th class=\"has-text-white\">\n <span>Row (term.id)</span>\n </th>\n </tr>\n </thead>\n <tbody class=\"has-text-white\">\n @for (data of missingLookups(); track dataIndex; let dataIndex = $index) {\n <tr>\n <td>{{ data.filename }}</td>\n <td>{{ data.column }}</td>\n <td>{{ data.termId }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n}\n\n<ng-template #sortColumn let-column=\"column\" let-sorting=\"sorting\">\n <a class=\"is-p-1 has-text-white\" (click)=\"toggleSort(column, sorting)\">\n @if (sorting.sortBy === column) {\n @if (sorting.sortOrder === 'asc') {\n <he-svg-icon name=\"sort-caret-up-filled\" size=\"16\" />\n } @else {\n <he-svg-icon name=\"sort-caret-down-filled\" size=\"16\" />\n }\n } @else {\n <he-svg-icon name=\"sort-caret-stacked-filled-stacked\" size=\"16\" />\n }\n </a>\n</ng-template>\n\n<ng-template #logLineArray let-value=\"value\" let-sorting=\"sorting\">\n <table class=\"table is-dark is-bordered is-striped\">\n @let headers = listHeaders(value);\n @if (headers.length) {\n <thead>\n <tr>\n @for (header of headers; track header) {\n <th class=\"has-text-white\">\n <div class=\"is-nowrap\">\n {{ header }}\n <ng-container *ngTemplateOutlet=\"sortColumn; context: { column: header, sorting }\" />\n </div>\n </th>\n }\n </tr>\n </thead>\n }\n <tbody>\n @for (v of value | sortBy: sorting.sortBy : sorting.sortOrder; track vIndex; let vIndex = $index) {\n <tr>\n @if (headers.length) {\n @for (header of headers; track header) {\n <td>{{ v[header] }}</td>\n }\n } @else {\n <td>{{ v }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n</ng-template>\n\n<ng-template #logLineValue let-key=\"key\" let-value=\"value\">\n @if (requirementLinkedNode(key, value); as linkedNode) {\n <he-node-link\n class=\"is-inline-block is-align-middle\"\n linkClass=\"is-dark\"\n [node]=\"linkedNode\"\n [showExternalLink]=\"true\" />\n } @else {\n <span class=\"is-inline-block is-align-middle\">{{ value }}</span>\n }\n</ng-template>\n\n<ng-template #logRow let-key=\"key\" let-value=\"value\" let-isCompleteness=\"isCompleteness\" let-complete=\"complete\">\n <tr>\n <td class=\"has-border-right | copy-log\">\n <span class=\"is-inline-block is-align-middle\">\n @if (isCompleteness) {\n <span>Data completeness for</span>\n <code class=\"is-mx-1\">{{ key }}</code>\n <span>must be</span>\n <code class=\"is-mx-1\">{{ complete }}</code>\n } @else {\n @switch (key) {\n @default {\n {{ parseKey(key) }}\n }\n @case ('property_id') {\n Property Term &#64;id\n }\n @case ('product_id') {\n Product Term &#64;id\n }\n @case ('crop_product_id') {\n Crop Product Term &#64;id\n }\n @case ('input_id') {\n Input Term &#64;id\n }\n @case ('node_type_allowed') {\n <span>Is the current</span>\n <code class=\"is-mx-1\">Node</code>\n <span>allowed to run this model</span>\n }\n @case ('siteType_allowed') {\n <span>Is the current</span>\n <code class=\"is-mx-1\">siteType</code>\n <span>allowed to run this model</span>\n }\n @case ('product_id_allowed') {\n <span>Are any of the Product</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('site_measurement_id_allowed') {\n <span>Are any of the Measurement</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('product_termType_allowed') {\n <span>Are any of the Product</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n @case ('input_id_allowed') {\n <span>Are any of the Input</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('input_termType_allowed') {\n <span>Are any of the Input</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n <!-- Deprecated -->\n @case ('primary_product_id_allowed') {\n <span>Is the current primary product</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('primary_product_termType_allowed') {\n <span>Is the current primary product</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n @case ('has_pesticides_inputs') {\n <span>Cycle contains</span>\n <code class=\"is-mx-1\">pesticideAI</code>\n <span>Inputs</span>\n }\n @case ('all_pesticideAI_have_lookup_value') {\n <span>All</span>\n <code class=\"is-mx-1\">pesticideAI</code>\n <span>Inputs have a lookup value</span>\n }\n @case ('has_crop_residue_burnt') {\n <code class=\"is-mr-1\">aboveGroundCropResidueBurnt</code>\n <span>is present as Product</span>\n }\n @case ('no_cycle_inputs_feed') {\n <span>\n Cycle has\n <b>no</b>\n Input with\n </span>\n <code>isAnimalFeed=true</code>\n }\n @case ('has_cycle_inputs_feed') {\n <span>\n Cycle has\n <b>some</b>\n Input with\n </span>\n <code>isAnimalFeed=true</code>\n }\n }\n }\n </span>\n <he-clipboard\n class=\"is-inline-block is-align-middle\"\n clipboardClass=\"is-size-7 is-p-1\"\n [value]=\"key\"\n [hideText]=\"true\" />\n </td>\n <td>\n @if (value | isArray) {\n <ng-container *ngTemplateOutlet=\"logLineArray; context: { value: value, sorting: {} }\" />\n } @else {\n <ng-container *ngTemplateOutlet=\"logLineValue; context: { key, value }\" />\n }\n </td>\n </tr>\n</ng-template>\n", styles: [".copy-log he-clipboard{visibility:hidden}.copy-log:hover he-clipboard{visibility:visible}\n"] }]
8806
+ args: [{ selector: 'he-node-logs-models-logs', imports: [NgTemplateOutlet, HESvgIconComponent, NodeLinkComponent, ClipboardComponent, SortByPipe, IsArrayPipe], template: "<div class=\"table-container\">\n <table class=\"table is-dark is-fullwidth\">\n <tbody>\n @for (log of allLogs(); track log.key) {\n <ng-container *ngTemplateOutlet=\"logRow; context: log\" />\n }\n </tbody>\n </table>\n</div>\n\n@if (missingLookups().length) {\n <div class=\"it-mt-2\">\n <p>Data missing (might be optional):</p>\n <div class=\"table-container data-table-container\">\n <table class=\"table is-dark is-fullwidth is-striped\">\n <thead>\n <tr>\n <th class=\"has-text-white\">\n <span>Filename</span>\n </th>\n <th class=\"has-text-white\">\n <span>Column Title</span>\n </th>\n <th class=\"has-text-white\">\n <span>Row (term.id)</span>\n </th>\n </tr>\n </thead>\n <tbody class=\"has-text-white\">\n @for (data of missingLookups(); track dataIndex; let dataIndex = $index) {\n <tr>\n <td>{{ data.filename }}</td>\n <td>{{ data.column }}</td>\n <td>{{ data.termId }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n}\n\n@if (raw()) {\n <div class=\"is-mt-2\">\n <div class=\"is-flex is-align-items-center is-gap-8\">\n <a class=\"has-text-white\" (click)=\"toggleRaw()\">\n <he-svg-icon [name]=\"showRaw() ? 'chevron-down' : 'chevron-right'\" size=\"16\" />\n <span>Raw log</span>\n </a>\n @if (showRaw()) {\n <he-clipboard clipboardClass=\"is-size-7 is-p-1\" [value]=\"rawJson()\" [hideText]=\"true\" />\n }\n </div>\n @if (showRaw()) {\n <pre class=\"raw-log is-mt-1\">{{ rawJson() }}</pre>\n }\n </div>\n}\n\n<ng-template #sortColumn let-column=\"column\" let-sorting=\"sorting\">\n <a class=\"is-p-1 has-text-white\" (click)=\"toggleSort(column, sorting)\">\n @if (sorting.sortBy === column) {\n @if (sorting.sortOrder === 'asc') {\n <he-svg-icon name=\"sort-caret-up-filled\" size=\"16\" />\n } @else {\n <he-svg-icon name=\"sort-caret-down-filled\" size=\"16\" />\n }\n } @else {\n <he-svg-icon name=\"sort-caret-stacked-filled-stacked\" size=\"16\" />\n }\n </a>\n</ng-template>\n\n<ng-template #logLineArray let-value=\"value\" let-sorting=\"sorting\">\n <table class=\"table is-dark is-bordered is-striped\">\n @let headers = listHeaders(value);\n @if (headers.length) {\n <thead>\n <tr>\n @for (header of headers; track header) {\n <th class=\"has-text-white\">\n <div class=\"is-nowrap\">\n {{ header }}\n <ng-container *ngTemplateOutlet=\"sortColumn; context: { column: header, sorting }\" />\n </div>\n </th>\n }\n </tr>\n </thead>\n }\n <tbody>\n @for (v of value | sortBy: sorting.sortBy : sorting.sortOrder; track vIndex; let vIndex = $index) {\n <tr>\n @if (headers.length) {\n @for (header of headers; track header) {\n <td>{{ v[header] }}</td>\n }\n } @else {\n <td>{{ v }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n</ng-template>\n\n<ng-template #logLineValue let-key=\"key\" let-value=\"value\">\n @if (requirementLinkedNode(key, value); as linkedNode) {\n <he-node-link\n class=\"is-inline-block is-align-middle\"\n linkClass=\"is-dark\"\n [node]=\"linkedNode\"\n [showExternalLink]=\"true\" />\n } @else {\n <span class=\"is-inline-block is-align-middle\">{{ value }}</span>\n }\n</ng-template>\n\n<ng-template #logRow let-key=\"key\" let-value=\"value\" let-isCompleteness=\"isCompleteness\" let-complete=\"complete\">\n <tr>\n <td class=\"has-border-right | copy-log\">\n <span class=\"is-inline-block is-align-middle\">\n @if (isCompleteness) {\n <span>Data completeness for</span>\n <code class=\"is-mx-1\">{{ key }}</code>\n <span>must be</span>\n <code class=\"is-mx-1\">{{ complete }}</code>\n } @else {\n @switch (key) {\n @default {\n {{ parseKey(key) }}\n }\n @case ('property_id') {\n Property Term &#64;id\n }\n @case ('product_id') {\n Product Term &#64;id\n }\n @case ('crop_product_id') {\n Crop Product Term &#64;id\n }\n @case ('input_id') {\n Input Term &#64;id\n }\n @case ('node_type_allowed') {\n <span>Is the current</span>\n <code class=\"is-mx-1\">Node</code>\n <span>allowed to run this model</span>\n }\n @case ('siteType_allowed') {\n <span>Is the current</span>\n <code class=\"is-mx-1\">siteType</code>\n <span>allowed to run this model</span>\n }\n @case ('product_id_allowed') {\n <span>Are any of the Product</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('site_measurement_id_allowed') {\n <span>Are any of the Measurement</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('product_termType_allowed') {\n <span>Are any of the Product</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n @case ('input_id_allowed') {\n <span>Are any of the Input</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('input_termType_allowed') {\n <span>Are any of the Input</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n <!-- Deprecated -->\n @case ('primary_product_id_allowed') {\n <span>Is the current primary product</span>\n <code class=\"is-mx-1\">&#64;id</code>\n <span>allowed to run this model</span>\n }\n @case ('primary_product_termType_allowed') {\n <span>Is the current primary product</span>\n <code class=\"is-mx-1\">termType</code>\n <span>allowed to run this model</span>\n }\n @case ('has_pesticides_inputs') {\n <span>Cycle contains</span>\n <code class=\"is-mx-1\">pesticideAI</code>\n <span>Inputs</span>\n }\n @case ('all_pesticideAI_have_lookup_value') {\n <span>All</span>\n <code class=\"is-mx-1\">pesticideAI</code>\n <span>Inputs have a lookup value</span>\n }\n @case ('has_crop_residue_burnt') {\n <code class=\"is-mr-1\">aboveGroundCropResidueBurnt</code>\n <span>is present as Product</span>\n }\n @case ('no_cycle_inputs_feed') {\n <span>\n Cycle has\n <b>no</b>\n Input with\n </span>\n <code>isAnimalFeed=true</code>\n }\n @case ('has_cycle_inputs_feed') {\n <span>\n Cycle has\n <b>some</b>\n Input with\n </span>\n <code>isAnimalFeed=true</code>\n }\n }\n }\n </span>\n <he-clipboard\n class=\"is-inline-block is-align-middle\"\n clipboardClass=\"is-size-7 is-p-1\"\n [value]=\"key\"\n [hideText]=\"true\" />\n </td>\n <td>\n @if (value | isArray) {\n <ng-container *ngTemplateOutlet=\"logLineArray; context: { value: value, sorting: {} }\" />\n } @else {\n <ng-container *ngTemplateOutlet=\"logLineValue; context: { key, value }\" />\n }\n </td>\n </tr>\n</ng-template>\n", styles: [".copy-log he-clipboard{visibility:hidden}.copy-log:hover he-clipboard{visibility:visible}.raw-log{max-height:300px;overflow:auto;white-space:pre;font-size:.75rem;background:#00000059;color:inherit;padding:.5rem;border-radius:3px}\n"] }]
8797
8807
  }], propDecorators: { logs: [{ type: i0.Input, args: [{ isSignal: true, alias: "logs", required: true }] }] } });
8798
8808
 
8799
8809
  class NodeLogsModelsLogsStatusComponent {
@@ -9138,19 +9148,27 @@ const groupLogsByModel = (data = '', groupBySubValue = false, groupByAnimal = fa
9138
9148
  ? groupLogSubValue(group, log, subValue)
9139
9149
  : groupLog(group, log, groupByAnimal);
9140
9150
  }, {});
9151
+ const logNodeFor = (node) => ({
9152
+ '@type': node['@type'],
9153
+ '@id': node['@id'],
9154
+ dataState: node.aggregated && node['@type'] !== NodeType.ImpactAssessment ? DataState.original : DataState.recalculated
9155
+ });
9141
9156
  class NodeLogsModelsService {
9142
9157
  constructor() {
9143
9158
  this.nodeService = inject(HeNodeService);
9144
9159
  }
9145
9160
  getNodeLogs$(node, groupByAnimal = false) {
9146
9161
  return this.nodeService
9147
- .getLog$({
9148
- '@type': node['@type'],
9149
- '@id': node['@id'],
9150
- dataState: node.aggregated && node['@type'] !== NodeType.ImpactAssessment ? DataState.original : DataState.recalculated
9151
- })
9162
+ .getLog$(logNodeFor(node))
9152
9163
  .pipe(map(logs => logs ? groupLogsByModel(logs, node['@type'] === NodeType.Cycle, groupByAnimal) : {}));
9153
9164
  }
9165
+ /**
9166
+ * Fetch the new `.jlog` (JSON) recalculation logs, following the representation of the node.
9167
+ * Resolves to an empty object when the node only has the legacy text logs.
9168
+ */
9169
+ getJLog$(node) {
9170
+ return this.nodeService.getLog$(logNodeFor(node), 'json').pipe(map(jlog => (jlog || {})));
9171
+ }
9154
9172
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeLogsModelsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
9155
9173
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeLogsModelsService, providedIn: 'root' }); }
9156
9174
  }
@@ -9161,6 +9179,889 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
9161
9179
  }]
9162
9180
  }] });
9163
9181
 
9182
+ /**
9183
+ * The `.jlog` recalculation logs follow the representation of the recalculated node: model logs are
9184
+ * stored per blank node index (with nested `properties`), plus `<field>-failed` entries (keyed by
9185
+ * term) for models that failed to add anything. Unlike the legacy text logs - which merge everything
9186
+ * by `term.@id` - this keeps every blank node distinct, so the same term can appear multiple times
9187
+ * (e.g. a Measurement at two different date ranges) and each is shown as its own sub-row.
9188
+ */
9189
+ const failedFieldSuffix = '-failed';
9190
+ const blankNodeModelId = (value) => (value?.methodModel || value?.method || value?.model)?.['@id'];
9191
+ // models that mark a blank node as "not relevant" rather than producing a value (mirrors the legacy list)
9192
+ const notRelevantModels = ['emissionNotRelevant'];
9193
+ const isNotRelevantModel = (methodId) => notRelevantModels.includes(methodId);
9194
+ // eslint-disable-next-line complexity -- exhaustive value-type mapping
9195
+ const jLogValue = (value) => value === true
9196
+ ? 'True'
9197
+ : value === false
9198
+ ? 'False'
9199
+ : value === null || typeof value === 'undefined'
9200
+ ? 'None'
9201
+ : Array.isArray(value)
9202
+ ? value
9203
+ : // a non-array object (e.g. an empty `{}`) is shown as readable text, not "[object Object]"
9204
+ typeof value === 'object'
9205
+ ? JSON.stringify(value)
9206
+ : String(value);
9207
+ // the orchestrator records these as model-debug logs (the legacy text format keeps them on a separate
9208
+ // line from the requirements). The `.jlog` flattens both onto the entry, so route the known ones to
9209
+ // `logs` - where the status/detail popovers read them (e.g. the "not relevant" breakdown)
9210
+ const modelLogKeys = [
9211
+ 'siteType_allowed',
9212
+ 'product_id_allowed',
9213
+ 'site_measurement_id_allowed',
9214
+ 'product_termType_allowed',
9215
+ 'input_id_allowed',
9216
+ 'input_termType_allowed',
9217
+ 'node_type_allowed',
9218
+ 'is_empty',
9219
+ 'error'
9220
+ ];
9221
+ // spread `obj` only when `condition` is truthy, otherwise nothing (for building objects declaratively)
9222
+ const spreadWhen = (condition, obj) => (condition ? obj : {});
9223
+ // the `key -> value` fields of an entry matching the predicate, with their values normalised
9224
+ const pickFields = (rest, predicate) => Object.fromEntries(Object.entries(rest)
9225
+ .filter(([key]) => predicate(key))
9226
+ .map(([key, value]) => [key, jLogValue(value)]));
9227
+ // the orchestrator's own status flags (`shouldRunOrchestrator`, and `runRequired` when the entry has none)
9228
+ const orchestratorFlags = (orchestrator, runRequired) => orchestrator && typeof orchestrator === 'object'
9229
+ ? {
9230
+ ...spreadWhen('should_run' in orchestrator, { shouldRunOrchestrator: orchestrator.should_run }),
9231
+ ...spreadWhen('run_required' in orchestrator && typeof runRequired === 'undefined', {
9232
+ runRequired: orchestrator.run_required
9233
+ })
9234
+ }
9235
+ : {};
9236
+ /**
9237
+ * Map a `.jlog` model run entry to the legacy `INodeTermLog` (status flags + `requirements` + `logs`),
9238
+ * so the requirements/logs and status detail components can render it unchanged.
9239
+ */
9240
+ const jLogEntryToLog = (entry) => {
9241
+ const { model, orchestrator, should_run, run_required, methodTier, model_key, is_not_relevant, ...rest } = entry;
9242
+ const logs = pickFields(rest, key => modelLogKeys.includes(key));
9243
+ const requirements = pickFields(rest, key => !modelLogKeys.includes(key));
9244
+ return {
9245
+ // keep the untouched `.jlog` entry so the popover can show the raw JSON
9246
+ raw: entry,
9247
+ // kept on the log itself (not in `requirements`) so the method tier shows next to the model name
9248
+ ...spreadWhen(methodTier, { methodTier }),
9249
+ ...spreadWhen(model_key, { model_key }),
9250
+ // surfaced at the top level so the status popover can read `is_not_relevant === 'True'`
9251
+ ...spreadWhen(typeof is_not_relevant !== 'undefined', { is_not_relevant: jLogValue(is_not_relevant) }),
9252
+ ...orchestratorFlags(orchestrator, run_required),
9253
+ ...spreadWhen(typeof should_run !== 'undefined', { shouldRun: should_run }),
9254
+ ...spreadWhen(typeof run_required !== 'undefined', { runRequired: run_required }),
9255
+ ...spreadWhen(Object.keys(logs).length, { logs }),
9256
+ ...spreadWhen(Object.keys(requirements).length, { requirements })
9257
+ };
9258
+ };
9259
+ /**
9260
+ * Derive the run status from the explicit `.jlog` fields:
9261
+ * - failed-section entries always count as an error;
9262
+ * - a model the orchestrator did not select (`orchestrator.should_run === false`) retained user data;
9263
+ * - a model that was selected but did not run failed - unless an earlier model in the hierarchy already
9264
+ * succeeded, in which case it was skipped (a higher-tier model ran instead).
9265
+ */
9266
+ // eslint-disable-next-line complexity -- a sequence of priority status rules
9267
+ const jLogModelStatus = (entry, isFailed, hasPreviousSuccess = false) => {
9268
+ if (isFailed)
9269
+ return LogStatus.error;
9270
+ if (entry?.run_required === false)
9271
+ return LogStatus.notRequired;
9272
+ if (entry?.orchestrator?.should_run === false)
9273
+ return LogStatus.dataProvided;
9274
+ if (entry?.should_run === true)
9275
+ return LogStatus.success;
9276
+ if (entry?.should_run === false)
9277
+ return hasPreviousSuccess ? LogStatus.skipHierarchy : LogStatus.error;
9278
+ return LogStatus.dataProvided;
9279
+ };
9280
+ // candidate engine-model-link queries, most precise first:
9281
+ // - the log's `model` + `orchestrator.value` map directly onto the link's `model` + `modelKey`;
9282
+ // - a dotted orchestrator value (e.g. `animal.pregnancyRateTotal`) may map to the bare key/term suffix
9283
+ // (`pregnancyRateTotal`) - links are keyed by `modelKey` or `term`, so try both;
9284
+ // - then the term-scoped matches, then the node-level model key (background models run at the node level,
9285
+ // e.g. `cycle`), and finally any model with that id
9286
+ const modelLinkQueries = (nodeType, termId, methodId, modelKey) => {
9287
+ const keySuffix = modelKey?.includes('.') ? modelKey.split('.').pop() : undefined;
9288
+ return [
9289
+ modelKey && { model: methodId, modelKey },
9290
+ keySuffix && { model: methodId, modelKey: keySuffix },
9291
+ keySuffix && { model: methodId, term: keySuffix },
9292
+ { '@type': nodeType, term: termId, model: methodId },
9293
+ { term: termId, model: methodId },
9294
+ { modelKey: toSnakeCase(nodeType), model: methodId },
9295
+ { model: methodId }
9296
+ ].filter(Boolean);
9297
+ };
9298
+ const resolveModel = (nodeType, termId, methodId, modelKey) => modelLinkQueries(nodeType, termId, methodId, modelKey)
9299
+ .map(query => findMatchingModel(query))
9300
+ .find(Boolean);
9301
+ const toModelRun = (entry, status, nodeType, termId) => {
9302
+ const log = jLogEntryToLog(entry);
9303
+ return {
9304
+ methodId: entry.model,
9305
+ modelKey: entry.orchestrator?.value,
9306
+ status,
9307
+ logs: log,
9308
+ showLogs: !!(log.requirements || log.logs || log.raw),
9309
+ model: resolveModel(nodeType, termId, entry.model, entry.orchestrator?.value)
9310
+ };
9311
+ };
9312
+ const jLogModelRuns = (logs, nodeType, termId, isFailed) => (logs || [])
9313
+ .flat()
9314
+ .filter(entry => !!entry?.model)
9315
+ // models run in hierarchy order, so thread "did an earlier model succeed?" through the runs: a later
9316
+ // model that did not run was skipped (a higher-tier model ran instead) rather than failed
9317
+ .reduce(({ runs, hasPreviousSuccess }, entry) => {
9318
+ const status = jLogModelStatus(entry, isFailed, hasPreviousSuccess);
9319
+ return {
9320
+ runs: [...runs, toModelRun(entry, status, nodeType, termId)],
9321
+ hasPreviousSuccess: hasPreviousSuccess || status === LogStatus.success
9322
+ };
9323
+ }, { runs: [], hasPreviousSuccess: false }).runs;
9324
+ // like `jLogModelRuns` but keeps the `.jlog` `logs` structure: each top-level entry is one "Model N"
9325
+ // column, and a nested array is a parallel group whose runs are stacked in that column (each independent,
9326
+ // so they share the same "previous success" state rather than skipping one another)
9327
+ const jLogModelColumns = (logs, nodeType, termId, isFailed) => (logs || [])
9328
+ .map(step => (Array.isArray(step) ? step : [step]).filter((entry) => !!entry?.model))
9329
+ .filter(entries => entries.length > 0)
9330
+ .reduce(({ columns, hasPreviousSuccess }, entries) => {
9331
+ const runs = entries.map((entry) => toModelRun(entry, jLogModelStatus(entry, isFailed, hasPreviousSuccess), nodeType, termId));
9332
+ return {
9333
+ columns: [...columns, runs.length > 1 ? runs : runs[0]],
9334
+ hasPreviousSuccess: hasPreviousSuccess || runs.some((run) => run.status === LogStatus.success)
9335
+ };
9336
+ }, { columns: [], hasPreviousSuccess: false }).columns;
9337
+ // `term.@id` defines the group; every other uniqueness field (including `key.@id`) distinguishes rows
9338
+ // within the group - Term-valued ones (key, inputs, country, landCover, methodModel, ...) become links
9339
+ const excludedIdentityFields = ['term.@id'];
9340
+ /**
9341
+ * The fields (from the schema `uniquenessFields`) that distinguish two blank nodes sharing a term.
9342
+ * Relations come from the type-keyed definition (e.g. `Emission` -> `inputs`); the scalar properties are
9343
+ * the union of that definition with the richer parent-keyed list (e.g. `Site.measurements`, which adds
9344
+ * `depthUpper`/`depthLower`), minus the term/key and any relation roots.
9345
+ */
9346
+ // eslint-disable-next-line complexity -- assembles the identity fields from two `uniquenessFields` sources
9347
+ const identitySpec = (type, nodeType, nodeKey) => {
9348
+ const def = (type && uniquenessFields[type]) || {};
9349
+ const relations = Object.keys(def).filter(key => key !== 'properties');
9350
+ const parentFields = (nodeType && uniquenessFields[nodeType]?.[nodeKey]) || [];
9351
+ const properties = [...new Set([...(def.properties || []), ...parentFields])].filter((field) => !excludedIdentityFields.includes(field) && !relations.includes(field.split('.')[0]));
9352
+ return { def, properties, relations };
9353
+ };
9354
+ const isPresent = (value) => value !== undefined && value !== null && value !== '';
9355
+ // the joined uniqueness-field ids of each item in a relation (e.g. an emission's `inputs[].@id`)
9356
+ const relationItemIds = (items, fields) => Array.isArray(items)
9357
+ ? items.map(item => fields.map(field => get(item, field)).filter(Boolean).join(':')).filter(Boolean)
9358
+ : [];
9359
+ const blankNodeIdentity = (value, { def, properties, relations }) => {
9360
+ const propertyEntries = properties
9361
+ .map((path) => [path, get(value, path)])
9362
+ .filter(([, v]) => isPresent(v));
9363
+ const relationEntries = relations
9364
+ .map((relation) => [relation, relationItemIds(value?.[relation], def[relation])])
9365
+ .filter(([, ids]) => ids.length);
9366
+ return Object.fromEntries([...propertyEntries, ...relationEntries]);
9367
+ };
9368
+ const identityKey = (identity) => JSON.stringify(identity);
9369
+ // is this a Term object (has an `@id` and looks like a Term), so it can be shown as a link
9370
+ const isTermLike = (value) => value?.['@type'] === NodeType.Term || !!value?.termType;
9371
+ const isTermObject = (value) => typeof value === 'object' && !Array.isArray(value) && !!value?.['@id'] && isTermLike(value);
9372
+ // the term(s) an identity field points to on the blank node (empty when the field is a scalar)
9373
+ const identityFieldTerms = (value, field) => {
9374
+ const target = value?.[field.split('.')[0]];
9375
+ return Array.isArray(target) ? target.filter(isTermObject) : isTermObject(target) ? [target] : [];
9376
+ };
9377
+ // method-metadata uniqueness fields are noise in the row identity (the model is already its own column)
9378
+ // unless they alone tell the rows apart
9379
+ const noisyIdentityFields = ['methodClassification', 'methodDescription', 'method.@id', 'methodModel.@id'];
9380
+ const isNoisyIdentityField = (field) => noisyIdentityFields.includes(field);
9381
+ const identityWithout = (identity, predicate) => Object.fromEntries(Object.entries(identity).filter(([key]) => !predicate(key)));
9382
+ const formatIdentityProperty = ([key, value]) => `${key.replace('.@id', '')}: ${Array.isArray(value) ? value.join(', ') : value}`;
9383
+ // the scalar identities as text; `depthUpper`/`depthLower` are combined into "depths: <upper>-<lower>"
9384
+ const formatIdentityLabel = (scalars) => {
9385
+ const props = Object.entries(scalars);
9386
+ const { depthUpper, depthLower } = Object.fromEntries(props);
9387
+ const hasDepths = isPresent(depthUpper) && isPresent(depthLower);
9388
+ const depths = hasDepths ? [`depths: ${depthUpper}-${depthLower}`] : [];
9389
+ const rest = props.filter(([key]) => !(hasDepths && (key === 'depthUpper' || key === 'depthLower')));
9390
+ return [...depths, ...rest.map(formatIdentityProperty)].join(' · ');
9391
+ };
9392
+ // whether dropping the noisy method fields still gives every row a distinct identity
9393
+ const noisyFieldsRedundant = (rows) => new Set(rows.map(row => identityKey(identityWithout(row.identity, isNoisyIdentityField)))).size === rows.length;
9394
+ // split a row's identity into Term-link segments (any field whose value is a Term) and a scalar text label
9395
+ const identityDisplay = (identity, value) => {
9396
+ const segments = [];
9397
+ const scalars = {};
9398
+ Object.entries(identity).forEach(([field, val]) => {
9399
+ const terms = identityFieldTerms(value, field);
9400
+ if (terms.length) {
9401
+ segments.push({ field: field.split('.')[0], terms });
9402
+ }
9403
+ else {
9404
+ scalars[field] = val;
9405
+ }
9406
+ });
9407
+ return { segments, label: formatIdentityLabel(scalars) };
9408
+ };
9409
+ // fill each row's identity segments + text label, dropping the noisy method fields unless they are the
9410
+ // only thing telling rows apart (a methodModel that distinguishes the rows is then shown as a link)
9411
+ const withRowLabels = (group) => {
9412
+ const keepNoisy = !noisyFieldsRedundant(group.rows);
9413
+ return {
9414
+ ...group,
9415
+ rows: group.rows.map(row => {
9416
+ const displayIdentity = keepNoisy ? row.identity : identityWithout(row.identity, isNoisyIdentityField);
9417
+ const { segments, label } = identityDisplay(displayIdentity, row.recalculated[0] || row.original[0]);
9418
+ return { ...row, identitySegments: segments, identityLabel: label };
9419
+ })
9420
+ };
9421
+ };
9422
+ const emptySubRow = () => ({
9423
+ index: 0,
9424
+ identity: {},
9425
+ identityLabel: '',
9426
+ identitySegments: [],
9427
+ originalValue: null,
9428
+ isOriginal: false,
9429
+ isRequired: true,
9430
+ isFailed: false,
9431
+ original: [],
9432
+ recalculated: [],
9433
+ subRows: [],
9434
+ canOpen: false,
9435
+ isOpen: false
9436
+ });
9437
+ const scalarValue = (value) => (['string', 'number', 'boolean'].includes(typeof value) ? value : null);
9438
+ /**
9439
+ * A scalar field of a blank node (e.g. `impactAssessment`, `price`, `economicValueShare`), shown as a
9440
+ * "Field: <key>" sub-row with the model(s) that gap-filled it.
9441
+ */
9442
+ const fieldSubRow = (key, nested, value, originalValue, nodeType, type) => {
9443
+ const updated = [...(value?.added || []), ...(value?.updated || [])];
9444
+ return {
9445
+ ...emptySubRow(),
9446
+ key,
9447
+ termId: key,
9448
+ type,
9449
+ originalValue: scalarValue(originalValue?.[key]),
9450
+ recalculatedValue: scalarValue(value?.[key]),
9451
+ isOriginal: typeof originalValue?.[key] !== 'undefined',
9452
+ isRecalculated: updated.includes(key),
9453
+ models: jLogModelRuns(nested?.logs, nodeType, key, false)
9454
+ };
9455
+ };
9456
+ // the model runs for a property: its logs usually live under the `value` field (`properties[i].value.logs`)
9457
+ const propertyModels = (childEntry, child, nodeType, termId) => {
9458
+ const valueFieldLogs = childEntry?.value?.logs;
9459
+ const rawModels = jLogModelRuns(valueFieldLogs || childEntry?.logs, nodeType, termId, false);
9460
+ // the `input.properties` model loads each property (with fallbacks), so it set the value: a
9461
+ // `should_run: false` on the value field is not a failure - the property was added, so it succeeded
9462
+ const logModels = valueFieldLogs
9463
+ ? rawModels.map(model => (model.status === LogStatus.error ? { ...model, status: LogStatus.success } : model))
9464
+ : rawModels;
9465
+ // when no model is logged, fall back to the model recorded on the property (its `methodModel`)
9466
+ const valueModelId = blankNodeModelId(child);
9467
+ return logModels.length || !valueModelId
9468
+ ? logModels
9469
+ : [syntheticModelRun(valueModelId, LogStatus.success, nodeType, termId)];
9470
+ };
9471
+ /**
9472
+ * Index-keyed nested blank nodes (e.g. `properties`), each mapped to its term and shown as a sub-row.
9473
+ */
9474
+ const nestedBlankNodeSubRows = (key, nested, value, nodeType) => Object.entries(nested || {})
9475
+ .map(([index, childEntry]) => {
9476
+ const child = value?.[key]?.[+index];
9477
+ const term = child?.term;
9478
+ if (!term?.['@id'])
9479
+ return null;
9480
+ const termId = term['@id'];
9481
+ return {
9482
+ ...emptySubRow(),
9483
+ index: +index,
9484
+ key: 'property',
9485
+ term,
9486
+ termId,
9487
+ type: SchemaType.Property,
9488
+ recalculatedValue: propertyValue$1(child?.value, termId),
9489
+ isRecalculated: true,
9490
+ recalculated: child ? [child] : [],
9491
+ models: propertyModels(childEntry, child, nodeType, termId)
9492
+ };
9493
+ })
9494
+ .filter(Boolean);
9495
+ const defaultGetTerm = (id) => ({ '@type': NodeType.Term, '@id': id, name: id });
9496
+ // a blank node's `<field>-failed` entries (e.g. an animal's `properties-failed`/`inputs-failed`): the
9497
+ // properties/inputs no model could add, shown as failed sub-rows keyed by term (resolved via `getTerm`)
9498
+ const failedNestedSubRows = (key, nested, nodeType, getTerm) => {
9499
+ const subKey = pluralize(key.replace(failedFieldSuffix, ''), 1);
9500
+ return Object.entries(nested || {}).map(([termId, childEntry]) => ({
9501
+ ...emptySubRow(),
9502
+ key: subKey,
9503
+ term: getTerm(termId),
9504
+ termId,
9505
+ type: subKey === 'property' ? SchemaType.Property : undefined,
9506
+ isFailed: true,
9507
+ models: jLogModelRuns(childEntry?.logs, nodeType, termId, true)
9508
+ }));
9509
+ };
9510
+ /**
9511
+ * The keys (property term ids and scalar field names) that have their own sub-row; the matching
9512
+ * orchestrator entries in `logs` are shown there, not as a model on the main row.
9513
+ */
9514
+ const subRowOrchestratorValues = (entry, value) => [
9515
+ ...Object.keys(entry?.properties || {})
9516
+ .map(index => value?.properties?.[+index]?.term?.['@id'])
9517
+ .filter(Boolean),
9518
+ ...Object.keys(entry || {}).filter(key => key !== 'logs' && Array.isArray(entry[key]?.logs))
9519
+ ];
9520
+ const isSubRowOrchestrator = (orchestratorValue, subRowValues) => !!orchestratorValue && (orchestratorValue.includes('.') || subRowValues.includes(orchestratorValue));
9521
+ // node keys for which the orchestrator config also lists models that could run (mirrors the legacy)
9522
+ const configModelKeys = ['measurements', 'practices', 'emissions', 'impacts'];
9523
+ const configModelMatches = (termId, nodeKey) => (model) => model.value === termId && pluralize(model.key, 2) === nodeKey;
9524
+ /**
9525
+ * The models the orchestrator config lists for a term (in config order), so models that could run after
9526
+ * a successful one are still shown.
9527
+ */
9528
+ const configModelsForTerm = (config, termId, nodeKey) => (config?.models || [])
9529
+ .flat(2)
9530
+ .filter(configModelMatches(termId, nodeKey))
9531
+ .map(model => model.model);
9532
+ /**
9533
+ * Models within the same orchestrator-config array run in parallel: each produces its own blank node, so
9534
+ * they are not fallbacks for one another and should not all be listed on every row (each row keeps only
9535
+ * the model that produced it). True when the term's config models all sit in a single parallel array.
9536
+ */
9537
+ const configModelsParallel = (config, termId, nodeKey) => {
9538
+ const matches = configModelMatches(termId, nodeKey);
9539
+ const groups = (config?.models || []).filter(group => (Array.isArray(group) ? group : [group]).some(matches));
9540
+ return groups.length === 1 && Array.isArray(groups[0]) && groups[0].filter(matches).length > 1;
9541
+ };
9542
+ const syntheticModelRun = (methodId, status, nodeType, termId) => ({ methodId, status, logs: {}, showLogs: false, model: resolveModel(nodeType, termId, methodId) });
9543
+ /**
9544
+ * The status of a config model that has no log: it succeeded if it set the value, was skipped if the
9545
+ * value was recalculated (by another model), provided when it came from the original, otherwise failed.
9546
+ */
9547
+ const configModelStatus = (methodId, valueModelId, isRecalculated, hasOriginal) => methodId === valueModelId
9548
+ ? LogStatus.success
9549
+ : isRecalculated
9550
+ ? LogStatus.skipHierarchy
9551
+ : hasOriginal
9552
+ ? LogStatus.dataProvided
9553
+ : LogStatus.error;
9554
+ // the `logs` entry that targets the value: the recorded model when present, otherwise (the value was
9555
+ // recalculated but the recorded model isn't in the logs - e.g. `hestiaAggregatedData` is computed by the
9556
+ // `linkedImpactAssessment` orchestrator) the first run that actually set the value
9557
+ const selectValueLog = (logs, valueModelId, isValueLog, isRecalculated) => logs.find((log) => log?.model === valueModelId && isValueLog(log)) ||
9558
+ (isRecalculated
9559
+ ? logs.find((log) => isValueLog(log) && (log?.should_run === true || log?.orchestrator?.should_run))
9560
+ : undefined);
9561
+ // model ids in display order: config models first, then any logged models, with "not relevant" models
9562
+ // (the actual outcome) pulled to the front
9563
+ const orderValueModelIds = (configModelIds, logModels) => [...new Set([...configModelIds, ...logModels.map(model => model.methodId)])].sort((a, b) => Number(isNotRelevantModel(b)) - Number(isNotRelevantModel(a)));
9564
+ // apply `update` to the model that set the value (matched by id) when `condition` holds, leaving the rest
9565
+ const mapValueModel = (models, valueModelId, condition, update) => models.map(model => (model.methodId === valueModelId && condition(model) ? update(model) : model));
9566
+ /**
9567
+ * A blank node's direct (value) model runs, in hierarchy order:
9568
+ * - the model that set the value is recorded on the recalculated blank node (`blankNodeModelId`);
9569
+ * - its matching `logs` entry (one targeting the value, not a sub-row) provides the run requirements,
9570
+ * together with any other model sharing that orchestrator value;
9571
+ * - for measurements/practices/emissions/impacts, the orchestrator config adds the models that could
9572
+ * run - the one that ran is successful, the others were skipped (a higher-tier model ran instead).
9573
+ *
9574
+ * When the value came from the original (no model, no config), the row shows no model.
9575
+ */
9576
+ // eslint-disable-next-line complexity -- orchestrates the value-model resolution (delegated to helpers)
9577
+ const valueModelRuns = (entry, value, originalValue, nodeType, termId, nodeKey, config) => {
9578
+ const valueModelId = blankNodeModelId(value);
9579
+ const isRecalculated = isRecalculatedValue(value, originalValue);
9580
+ const hasOriginal = !!originalValue && typeof originalValue.value !== 'undefined';
9581
+ const valueFromOriginal = valueRetainedFromOriginal(value, originalValue);
9582
+ const subRowValues = subRowOrchestratorValues(entry, value);
9583
+ const logs = (entry?.logs || []).flat();
9584
+ // background-tier logs belong to an input's "Background Data" sub-row - unless the blank node is itself
9585
+ // a background emission, in which case its value model IS the background run (e.g. `bafu2025`)
9586
+ const valueIsBackground = value?.methodTier === EmissionMethodTier.background;
9587
+ const isBackgroundLog = (log) => !valueIsBackground && log?.methodTier === EmissionMethodTier.background;
9588
+ const isValueLog = (log) => !isBackgroundLog(log) && !isSubRowOrchestrator(log?.orchestrator?.value, subRowValues);
9589
+ const valueLog = selectValueLog(logs, valueModelId, isValueLog, isRecalculated);
9590
+ const valueOrchestrator = valueLog?.orchestrator?.value;
9591
+ const rawLogModels = valueLog
9592
+ ? jLogModelRuns(logs.filter((log) => log?.orchestrator?.value === valueOrchestrator && !isBackgroundLog(log)), nodeType, termId, false)
9593
+ : [];
9594
+ // the recorded value model (the blank node `methodModel`, e.g. `hestiaAggregatedData`) can differ from
9595
+ // the single orchestrator model in the logs (e.g. `linkedImpactAssessment`); relabel that run to the
9596
+ // recorded model so it shows under the expected name, but keep the orchestrator run's logs and its
9597
+ // resolved guide link (the recorded model - an aggregation source - has no model link of its own)
9598
+ const logModels = valueModelId && rawLogModels.length === 1 && rawLogModels[0].methodId !== valueModelId
9599
+ ? [{ ...rawLogModels[0], methodId: valueModelId }]
9600
+ : rawLogModels;
9601
+ // parallel config models each produce their own blank node, so they are not listed together on a row
9602
+ const configModelIds = config && configModelKeys.includes(nodeKey) && !configModelsParallel(config, termId, nodeKey)
9603
+ ? configModelsForTerm(config, termId, nodeKey)
9604
+ : [];
9605
+ const logByMethodId = new Map(logModels.map(model => [model.methodId, model]));
9606
+ const models = orderValueModelIds(configModelIds, logModels).map(methodId => logByMethodId.get(methodId) ||
9607
+ syntheticModelRun(methodId, configModelStatus(methodId, valueModelId, isRecalculated, hasOriginal), nodeType, termId));
9608
+ // the value model (e.g. a measured method gap-filled this run) is not always in the logs or config
9609
+ const baseModels = models.length || !valueModelId || valueFromOriginal
9610
+ ? models
9611
+ : [syntheticModelRun(valueModelId, LogStatus.success, nodeType, termId)];
9612
+ // the model recorded on the node set the value this run, so it ran successfully - even when its log
9613
+ // omits `should_run` (e.g. the `seed_emissions` orchestrator), which falls through to "data retained"
9614
+ const withSuccess = mapValueModel(baseModels, valueModelId, model => isRecalculated && !valueFromOriginal && model.status === LogStatus.dataProvided, model => ({ ...model, status: LogStatus.success }));
9615
+ // the value model carries the blank node's method tier (e.g. an emission tier) even when its log omits
9616
+ // it, so it shows next to the model name like the other tier emissions
9617
+ const valueMethodTier = value?.methodTier;
9618
+ return mapValueModel(withSuccess, valueModelId, model => !!valueMethodTier && !model.logs?.methodTier, model => ({ ...model, logs: { ...(model.logs || {}), methodTier: valueMethodTier } }));
9619
+ };
9620
+ /**
9621
+ * Build a sub-row for every nested key of a blank node entry (all keys except `logs`): scalar fields
9622
+ * become "Field: <key>" rows; index-keyed nested blank nodes (e.g. `properties`) become term sub-rows.
9623
+ * Field sub-rows are listed before property sub-rows.
9624
+ */
9625
+ const isFieldEntry = ([, nested]) => Array.isArray(nested?.logs);
9626
+ const isPropertyEntry = ([, nested]) => !Array.isArray(nested?.logs) && nested && typeof nested === 'object';
9627
+ const isFailedFieldEntry = ([key]) => key.endsWith(failedFieldSuffix);
9628
+ const blankNodeSubRows = (entry, value, originalValue, nodeType, type, getTerm) => {
9629
+ // a scalar field (with its own `logs`) becomes a "Field:" sub-row; an index-keyed nested node (e.g.
9630
+ // `properties`) expands into "Property:" sub-rows; `<field>-failed` entries become failed sub-rows.
9631
+ const entries = Object.entries(entry || {}).filter(([key]) => key !== 'logs');
9632
+ const fields = entries
9633
+ .filter(entryItem => !isFailedFieldEntry(entryItem) && isFieldEntry(entryItem))
9634
+ .map(([key, nested]) => fieldSubRow(key, nested, value, originalValue, nodeType, type));
9635
+ const properties = entries
9636
+ .filter(entryItem => !isFailedFieldEntry(entryItem) && isPropertyEntry(entryItem))
9637
+ .flatMap(([key, nested]) => nestedBlankNodeSubRows(key, nested, value, nodeType));
9638
+ const failed = entries
9639
+ .filter(isFailedFieldEntry)
9640
+ .flatMap(([key, nested]) => failedNestedSubRows(key, nested, nodeType, getTerm));
9641
+ return [...fields, ...properties, ...failed];
9642
+ };
9643
+ /**
9644
+ * An input's background-emission models (`methodTier: background`), grouped under a single
9645
+ * "Background Data" sub-row.
9646
+ */
9647
+ const backgroundDataSubRow = (inputEntries, nodeType, termId) => {
9648
+ const backgroundEntries = inputEntries.filter(log => log?.methodTier === EmissionMethodTier.background);
9649
+ return backgroundEntries.length
9650
+ ? {
9651
+ ...emptySubRow(),
9652
+ key: 'backgroundData',
9653
+ termId: 'backgroundData',
9654
+ recalculatedValue: null,
9655
+ isRecalculated: false,
9656
+ models: jLogModelRuns(backgroundEntries, nodeType, termId, false)
9657
+ }
9658
+ : null;
9659
+ };
9660
+ // the value was set/changed this run (listed in `added`/`updated`)
9661
+ const valueChanged = (value) => [...(value?.added || []), ...(value?.updated || [])].includes('value');
9662
+ // a value present in the original and not changed this run was retained (even if it keeps a `methodModel`,
9663
+ // e.g. a measured emission method) - it was not recalculated
9664
+ const valueRetainedFromOriginal = (value, originalValue) => !!originalValue && typeof originalValue.value !== 'undefined' && !valueChanged(value);
9665
+ const isRecalculatedValue = (value, originalValue) => valueChanged(value) || (!!blankNodeModelId(value) && !valueRetainedFromOriginal(value, originalValue));
9666
+ // every sub-row of a (non-failed) blank node: its field/property sub-rows, plus - for an input - the
9667
+ // "Background Data" sub-row holding its background-emission models
9668
+ const allSubRows = (entry, value, originalValue, nodeType, type, termId, getTerm) => {
9669
+ const isInput = type === SchemaType.Input;
9670
+ const inputEntries = isInput ? (entry?.logs || []).flat().filter((e) => !!e?.model) : [];
9671
+ const backgroundData = isInput ? backgroundDataSubRow(inputEntries, nodeType, termId) : null;
9672
+ return [
9673
+ ...blankNodeSubRows(entry, value, originalValue, nodeType, type, getTerm),
9674
+ ...(backgroundData ? [backgroundData] : [])
9675
+ ];
9676
+ };
9677
+ const flattenColumns = (columns) => columns.flatMap(column => (Array.isArray(column) ? column : [column]));
9678
+ // a `<key>-failed` entry has no value: its identity/sub-rows are empty and the models that tried are
9679
+ // errors; parallel models (a nested `logs` array) are kept grouped (`modelColumns`) so they stack in one
9680
+ // column, while `models` stays flat for the status checks
9681
+ const failedBlankNodeContent = (entry, nodeType, termId) => {
9682
+ const columns = jLogModelColumns(entry?.logs, nodeType, termId, true);
9683
+ return {
9684
+ recalculatedValue: null,
9685
+ identity: {},
9686
+ identityLabel: '',
9687
+ identitySegments: [],
9688
+ subRows: [],
9689
+ isRecalculated: false,
9690
+ recalculated: [],
9691
+ models: flattenColumns(columns),
9692
+ modelColumns: columns
9693
+ };
9694
+ };
9695
+ // a regular blank node: its identity, sub-rows and the model(s) that set the value (or none when retained)
9696
+ const valueBlankNodeContent = (entry, value, originalValue, termId, { nodeType, type, nodeKey, config, getTerm }) => {
9697
+ const spec = identitySpec(type, nodeType, nodeKey);
9698
+ return {
9699
+ recalculatedValue: propertyValue$1(value?.value, termId),
9700
+ identity: blankNodeIdentity(value, spec),
9701
+ // `identityLabel`/`identitySegments` are filled per group (`withRowLabels`), which needs all rows
9702
+ identityLabel: '',
9703
+ identitySegments: [],
9704
+ subRows: allSubRows(entry, value, originalValue, nodeType, type, termId, getTerm || defaultGetTerm),
9705
+ isRecalculated: isRecalculatedValue(value, originalValue),
9706
+ recalculated: value ? [value] : [],
9707
+ models: valueModelRuns(entry, value, originalValue, nodeType, termId, nodeKey, config)
9708
+ };
9709
+ };
9710
+ const buildBlankNode = (entry, value, originalValue, index, term, isFailed, context) => {
9711
+ const termId = term['@id'];
9712
+ return {
9713
+ index,
9714
+ term,
9715
+ termId,
9716
+ type: context.type,
9717
+ originalValue: originalValue ? propertyValue$1(originalValue.value, termId) : null,
9718
+ isOriginal: !!originalValue && typeof originalValue.value !== 'undefined',
9719
+ isRequired: !isFailed,
9720
+ isFailed,
9721
+ original: originalValue ? [originalValue] : [],
9722
+ canOpen: false,
9723
+ isOpen: false,
9724
+ ...(isFailed
9725
+ ? failedBlankNodeContent(entry, context.nodeType, termId)
9726
+ : valueBlankNodeContent(entry, value, originalValue, termId, context))
9727
+ };
9728
+ };
9729
+ /**
9730
+ * Group the `.jlog` logs for one node key into rows, one per term, keeping every blank node distinct.
9731
+ *
9732
+ * @param jlog The full parsed `.jlog`.
9733
+ * @param nodeKey The node field being shown (e.g. `inputs`, `measurements`).
9734
+ * @param recalculatedValues The recalculated blank nodes for that key (index-aligned with the `.jlog`).
9735
+ * @param originalValues The original blank nodes for that key (matched by uniqueness for the delta).
9736
+ * @param nodeType The node type.
9737
+ * @param getTerm Resolve a term by `@id` (used for `<key>-failed` entries that have no blank node).
9738
+ * @param config The orchestrator config, used to list models that could run for a term.
9739
+ */
9740
+ // eslint-disable-next-line complexity -- builds the type/term context then delegates to helpers
9741
+ const groupJLogByTerm = (jlog, nodeKey, recalculatedValues = [], originalValues = [], nodeType = undefined, getTerm = id => ({ '@type': NodeType.Term, '@id': id, name: id }), config) => {
9742
+ const keyLogs = jlog?.[nodeKey] || {};
9743
+ const failedLogs = jlog?.[`${nodeKey}${failedFieldSuffix}`] || {};
9744
+ const type = recalculatedValues?.[0]?.['@type'] ||
9745
+ recalculatedValues?.[0]?.type ||
9746
+ originalValues?.[0]?.['@type'];
9747
+ const context = { nodeType, type, nodeKey, config, getTerm };
9748
+ const spec = identitySpec(type, nodeType, nodeKey);
9749
+ const matchOriginal = (value) => {
9750
+ const key = identityKey(blankNodeIdentity(value, spec));
9751
+ return (originalValues || []).find(original => original.term?.['@id'] === value.term?.['@id'] && identityKey(blankNodeIdentity(original, spec)) === key);
9752
+ };
9753
+ const blankNodes = [
9754
+ // list every blank node, attaching its `.jlog` logs when present (a node may exist without logs,
9755
+ // e.g. a value provided in the original)
9756
+ ...(recalculatedValues || []).map((value, index) => {
9757
+ const term = value?.term;
9758
+ return term?.['@id']
9759
+ ? buildBlankNode(keyLogs[index] || {}, value, matchOriginal(value), index, term, false, context)
9760
+ : null;
9761
+ }),
9762
+ ...Object.entries(failedLogs).map(([termId, entry], i) => buildBlankNode(entry, undefined, undefined, -1 - i, getTerm(termId), true, context))
9763
+ ].filter(Boolean);
9764
+ return orderGroups(groupBlankNodesByTerm(blankNodes)
9765
+ .map(withCollapsible)
9766
+ .map(withRowLabels), nodeKey);
9767
+ };
9768
+ // group blank nodes by their term id, preserving first-seen order
9769
+ const groupBlankNodesByTerm = (blankNodes) => blankNodes.reduce((groups, blankNode) => {
9770
+ const group = groups.find(g => g.termId === blankNode.termId);
9771
+ if (group) {
9772
+ group.rows.push(blankNode);
9773
+ return groups;
9774
+ }
9775
+ const { term, termId, type } = blankNode;
9776
+ return [
9777
+ ...groups,
9778
+ { term, termId, type, rows: [blankNode], originalValue: null, recalculatedValue: null, canOpen: false, isOpen: false }
9779
+ ];
9780
+ }, []);
9781
+ // combine (sum) the `value` of several blank nodes into one (mirrors the legacy `reduceValues`)
9782
+ const reduceValues = (values, termId) => {
9783
+ const propertyValues = values.map(({ value }) => propertyValue$1(value, termId)).filter(v => v !== null);
9784
+ return propertyValues.length ? propertyValue$1(propertyValues, termId) : null;
9785
+ };
9786
+ // a group with several blank nodes (or with nested sub-rows) is collapsible, as are its rows with sub-rows;
9787
+ // its header shows the combined value across all rows
9788
+ const withCollapsible = (group) => ({
9789
+ ...group,
9790
+ canOpen: group.rows.length > 1 || group.rows.some(row => row.subRows.length > 0),
9791
+ rows: group.rows.map(row => ({ ...row, canOpen: row.subRows.length > 0 })),
9792
+ originalValue: reduceValues(group.rows.flatMap(row => row.original), group.termId),
9793
+ recalculatedValue: reduceValues(group.rows.flatMap(row => row.recalculated), group.termId)
9794
+ });
9795
+ const groupName = (group) => group.term?.name || group.termId;
9796
+ const groupMethodTier = (group) => methodTierOrder(group.rows[0]?.recalculated?.[0]?.methodTier);
9797
+ // emissions are ordered by method tier (like the legacy component), everything else by term name
9798
+ const orderGroups = (groups, nodeKey) => nodeKey === 'emissions'
9799
+ ? orderBy(groups, [groupMethodTier, groupName], ['asc', 'asc'])
9800
+ : orderBy(groups, [groupName], ['asc']);
9801
+ /**
9802
+ * Group the `.jlog` logs for a metadata or completeness node (a `key -> value` map, not blank nodes).
9803
+ *
9804
+ * Each key is one row: the model logs come from the field's own `.jlog` entry (e.g. `startDate`), or
9805
+ * from the shared node-key entry for completeness (`completeness`). Values come from the `key -> value`
9806
+ * original/recalculated maps.
9807
+ *
9808
+ * @param jlog The full parsed `.jlog`.
9809
+ * @param nodeKey The node field being shown (`completeness`, or empty for cycle metadata).
9810
+ * @param originalValues The original `key -> value` map.
9811
+ * @param recalculatedValues The recalculated `key -> value` map.
9812
+ * @param nodeType The node type.
9813
+ */
9814
+ const groupJLogByField = (jlog, nodeKey, originalValues, recalculatedValues, nodeType = undefined) => {
9815
+ const updated = [
9816
+ ...(recalculatedValues?.added || []),
9817
+ ...(recalculatedValues?.updated || [])
9818
+ ];
9819
+ // eslint-disable-next-line complexity -- maps each field key to a fully-populated row
9820
+ return computeKeys(originalValues, recalculatedValues).map(key => {
9821
+ // for a node-key field map (e.g. `completeness`) the per-field logs are nested under
9822
+ // `jlog[nodeKey][key]`; the node-key's own top-level `logs` are ignored. metadata fields are top-level
9823
+ const entry = nodeKey ? jlog?.[nodeKey]?.[key] : jlog?.[key];
9824
+ const originalValue = originalValues?.[key];
9825
+ const recalculatedValue = recalculatedValues?.[key];
9826
+ const row = {
9827
+ index: 0,
9828
+ termId: key,
9829
+ key,
9830
+ identity: {},
9831
+ identityLabel: '',
9832
+ identitySegments: [],
9833
+ originalValue: originalValue ?? null,
9834
+ recalculatedValue: recalculatedValue ?? null,
9835
+ isOriginal: typeof originalValue !== 'undefined',
9836
+ isRecalculated: updated.includes(key) || (recalculatedValue !== undefined && recalculatedValue !== originalValue),
9837
+ isRequired: true,
9838
+ isFailed: false,
9839
+ original: [],
9840
+ recalculated: [],
9841
+ models: jLogModelRuns(entry?.logs, nodeType, key, false),
9842
+ subRows: [],
9843
+ canOpen: false,
9844
+ isOpen: false
9845
+ };
9846
+ return {
9847
+ termId: key,
9848
+ key,
9849
+ rows: [row],
9850
+ originalValue: null,
9851
+ recalculatedValue: null,
9852
+ canOpen: false,
9853
+ isOpen: false
9854
+ };
9855
+ });
9856
+ };
9857
+ // the number of "Model N" columns a row needs (a parallel group counts as one column)
9858
+ const rowColumnCount = (row) => (row.modelColumns ?? row.models).length;
9859
+ /**
9860
+ * The maximum number of model columns across all rows, to size the "Model N" columns.
9861
+ */
9862
+ const jLogModelCount = (groups) => Math.max(0, ...groups.flatMap(group => group.rows.flatMap(row => [rowColumnCount(row), ...row.subRows.map(rowColumnCount)])));
9863
+
9864
+ const groupTerms$1 = (terms) => terms.reduce((prev, curr) => ({ ...prev, [curr['@id']]: curr }), {});
9865
+ const logIcon$1 = {
9866
+ [LogStatus.success]: 'checkmark',
9867
+ [LogStatus.error]: 'xmark',
9868
+ [LogStatus.skipHierarchy]: 'circle',
9869
+ [LogStatus.dataProvided]: 'dot-filled',
9870
+ [LogStatus.notRequired]: 'dot-circle'
9871
+ };
9872
+ const logColor$1 = {
9873
+ [LogStatus.success]: 'success',
9874
+ [LogStatus.error]: 'danger',
9875
+ [LogStatus.skipHierarchy]: 'dark',
9876
+ [LogStatus.dataProvided]: 'dark',
9877
+ [LogStatus.notRequired]: 'grey'
9878
+ };
9879
+ const methodIdLabel$1 = (methodId) => (methodId
9880
+ ? { 'cycle/transformation': 'Data From Transformation', impact_assessment: 'Data From Cycle' }[methodId]
9881
+ : '') || (methodId.includes('/') ? methodId.split('/')[0] : keyToLabel(methodId));
9882
+ // some runs are better described by the orchestrator value (model key) than by the model id (e.g. `cycle`)
9883
+ const modelKeyLabel = {
9884
+ 'input.hestiaAggregatedData': 'HESTIA aggregated data'
9885
+ };
9886
+ const termSearch$1 = (term = '') => term.replace(/[,\s]/g, '').toLowerCase();
9887
+ class NodeJLogModelsComponent {
9888
+ constructor() {
9889
+ this.searchService = inject(HeSearchService);
9890
+ this.engineService = inject(HeEngineService);
9891
+ this.guideEnabled = inject(GUIDE_ENABLED);
9892
+ this.node = input.required(...(ngDevMode ? [{ debugName: "node" }] : []));
9893
+ this.nodeKey = input(...(ngDevMode ? [undefined, { debugName: "nodeKey" }] : []));
9894
+ this.originalValues = input([], ...(ngDevMode ? [{ debugName: "originalValues" }] : []));
9895
+ this.recalculatedValues = input([], ...(ngDevMode ? [{ debugName: "recalculatedValues" }] : []));
9896
+ this.filterTermTypes = input([], ...(ngDevMode ? [{ debugName: "filterTermTypes" }] : []));
9897
+ // a single label for the filtered term types (e.g. "Management"), shown instead of joining them all
9898
+ this.filterTermTypesLabel = input('', ...(ngDevMode ? [{ debugName: "filterTermTypesLabel" }] : []));
9899
+ this.cycle = input(...(ngDevMode ? [undefined, { debugName: "cycle" }] : []));
9900
+ this.jlog = input({}, ...(ngDevMode ? [{ debugName: "jlog" }] : []));
9901
+ // for a grouped sub-node view (e.g. an animal's inputs), the `.jlog` is nested under its parent: scope
9902
+ // to `jlog[<parentKey>][<parentIndex>]` (e.g. `jlog.animals.0`) so `nodeKey` reads the right section
9903
+ this.jlogParentKey = input(...(ngDevMode ? [undefined, { debugName: "jlogParentKey" }] : []));
9904
+ this.jlogParentIndex = input(...(ngDevMode ? [undefined, { debugName: "jlogParentIndex" }] : []));
9905
+ // the `.jlog` section the rows are read from - the whole jlog, or the parent-scoped sub-node entry
9906
+ this.scopedJlog = computed(() => {
9907
+ const key = this.jlogParentKey();
9908
+ const index = this.jlogParentIndex();
9909
+ return key && typeof index === 'number' && index >= 0 ? (this.jlog()?.[key]?.[index] ?? {}) : this.jlog();
9910
+ }, ...(ngDevMode ? [{ debugName: "scopedJlog" }] : []));
9911
+ this.schemaBaseUrl = schemaBaseUrl();
9912
+ this.isExternal = isExternal();
9913
+ this.LogStatus = LogStatus;
9914
+ this.logIcon = logIcon$1;
9915
+ this.logColor = logColor$1;
9916
+ this.isInSystemBoundary = isInSystemBoundary;
9917
+ this.typeaheadFocus = typeaheadFocus;
9918
+ this.isNumber = (value) => typeof value === 'number';
9919
+ this.isEmptyValue = (value) => value === null || value === undefined || value === '';
9920
+ this.onlyRequired = signal(true, ...(ngDevMode ? [{ debugName: "onlyRequired" }] : []));
9921
+ this.term = signal(undefined, ...(ngDevMode ? [{ debugName: "term" }] : []));
9922
+ this.nodeType = computed(() => nodeType(this.node()), ...(ngDevMode ? [{ debugName: "nodeType" }] : []));
9923
+ // blank nodes are passed as arrays; metadata/completeness as a `key -> value` map
9924
+ this.isBlankNodes = computed(() => Array.isArray(this.originalValues()) || Array.isArray(this.recalculatedValues()), ...(ngDevMode ? [{ debugName: "isBlankNodes" }] : []));
9925
+ // resolve names for `<key>-failed` terms (top-level, and the `<field>-failed` nested inside each blank
9926
+ // node, e.g. an animal's failed properties) so they render as proper term links
9927
+ this.failedTermIds = computed(() => {
9928
+ if (!this.isBlankNodes())
9929
+ return [];
9930
+ const jlog = this.scopedJlog() || {};
9931
+ const topLevel = Object.keys(jlog[`${this.nodeKey()}-failed`] || {});
9932
+ const nested = Object.values(jlog[this.nodeKey()] || {}).flatMap((entry) => Object.entries(entry || {})
9933
+ .filter(([key]) => key.endsWith('-failed'))
9934
+ .flatMap(([, failedEntry]) => Object.keys(failedEntry || {})));
9935
+ return unique([...topLevel, ...nested].filter(Boolean));
9936
+ }, ...(ngDevMode ? [{ debugName: "failedTermIds" }] : []));
9937
+ this.failedTermsResource = rxResource({
9938
+ params: () => ({ ids: this.failedTermIds() }),
9939
+ stream: ({ params: { ids } }) => ids.length
9940
+ ? this.searchTerms({
9941
+ bool: { must: [matchType(NodeType.Term)], should: ids.map(matchId), minimum_should_match: 1 }
9942
+ }).pipe(map(groupTerms$1))
9943
+ : of({})
9944
+ });
9945
+ this.failedTerms = computed(() => this.failedTermsResource.value() ?? {}, ...(ngDevMode ? [{ debugName: "failedTerms" }] : []));
9946
+ // orchestrator config lists the models that could run for a term (so skipped models are still shown)
9947
+ this.configResource = rxResource({
9948
+ params: () => ({ node: this.node() }),
9949
+ stream: ({ params: { node } }) => this.engineService.ochestratorConfig$(nodeType(node), node['@id'] || node.id)
9950
+ });
9951
+ this.config = computed(() => this.configResource.value(), ...(ngDevMode ? [{ debugName: "config" }] : []));
9952
+ this.allGroups = computed(() => {
9953
+ if (!this.isBlankNodes()) {
9954
+ return groupJLogByField(this.scopedJlog(), this.nodeKey(), this.originalValues(), this.recalculatedValues(), this.nodeType());
9955
+ }
9956
+ const groups = groupJLogByTerm(this.scopedJlog(), this.nodeKey(), this.recalculatedValues(), this.originalValues(), this.nodeType(), id => this.failedTerms()[id] || { '@type': NodeType.Term, '@id': id, name: termTypeLabel(id) }, this.config());
9957
+ // a combined node key (e.g. `emissionsResourceUse`) holds several term types; keep only the requested
9958
+ // ones (like the legacy `computeTerms`), keeping terms whose type is not yet resolved
9959
+ const termTypes = this.filterTermTypes();
9960
+ return termTypes?.length
9961
+ ? groups.filter(group => !group.term?.termType || termTypes.includes(group.term.termType))
9962
+ : groups;
9963
+ }, ...(ngDevMode ? [{ debugName: "allGroups" }] : []));
9964
+ this.groups = computed(() => this.allGroups().filter(group => {
9965
+ const selected = this.term();
9966
+ if (selected)
9967
+ return group.term?.name?.toLowerCase().includes(selected.name.toLowerCase());
9968
+ // hide non-system-boundary terms with no recalculation when "only required" is on
9969
+ return (!this.onlyRequired() ||
9970
+ !this.filteredType() ||
9971
+ isInSystemBoundary(group.termId) ||
9972
+ group.rows.some(row => row.isRecalculated || row.isFailed));
9973
+ }), ...(ngDevMode ? [{ debugName: "groups" }] : []));
9974
+ this.hasLogs = computed(() => !isEmpty(this.jlog()), ...(ngDevMode ? [{ debugName: "hasLogs" }] : []));
9975
+ this.methodModelsCount = computed(() => Math.max(1, jLogModelCount(this.allGroups())), ...(ngDevMode ? [{ debugName: "methodModelsCount" }] : []));
9976
+ this.filteredType = computed(() => this.filterTermTypesLabel() || this.filterTermTypes()?.map(termTypeLabel).join(' & '), ...(ngDevMode ? [{ debugName: "filteredType" }] : []));
9977
+ this.allTerms = computed(() => this.allGroups().map(group => group.term).filter(Boolean), ...(ngDevMode ? [{ debugName: "allTerms" }] : []));
9978
+ this.enableFilterByTerm = computed(() => this.allTerms().length > 0, ...(ngDevMode ? [{ debugName: "enableFilterByTerm" }] : []));
9979
+ this.termFormatter = ({ name }) => name;
9980
+ this.suggestTerm = (text$) => text$.pipe(distinctUntilChanged(), map(value => this.allTerms().filter(v => termSearch$1(v.name).includes(termSearch$1(value)))));
9981
+ this.methodsById = toSignal(this.searchTerms({
9982
+ bool: { must: [matchType(NodeType.Term), { match: { termType: TermTermType.model } }] }
9983
+ }).pipe(map(groupTerms$1)));
9984
+ this.isArray = Array.isArray;
9985
+ this.succeeded = (model) => [LogStatus.success, LogStatus.notRequired, LogStatus.dataProvided].includes(model.status);
9986
+ }
9987
+ searchTerms(query) {
9988
+ return this.searchService
9989
+ .search$({
9990
+ fields: ['@type', '@id', 'name', 'units', 'termType'],
9991
+ limit: 10000,
9992
+ query
9993
+ })
9994
+ .pipe(map(({ results }) => results));
9995
+ }
9996
+ methodName({ methodId, modelKey }) {
9997
+ return modelKeyLabel[modelKey] || this.methodsById()?.[methodId]?.name || methodIdLabel$1(methodId);
9998
+ }
9999
+ modelMethodTier(model) {
10000
+ return model.logs?.methodTier || model.model?.methodTier;
10001
+ }
10002
+ // the per-column view of a row's models (a column may be a parallel group of runs, shown stacked)
10003
+ modelColumns(row) {
10004
+ return row.modelColumns ?? row.models;
10005
+ }
10006
+ // a collapsed row with sub-rows but no direct model "all succeeded" when each sub-row has a run that
10007
+ // did not fail (mirrors the legacy `hasCompleteSuccess`)
10008
+ subRowsAllSucceeded(row) {
10009
+ return row.subRows.every(sub => sub.models.some(this.succeeded));
10010
+ }
10011
+ // a collapsed multi-row group "all succeeded" when every one of its rows succeeded
10012
+ groupAllSucceeded(group) {
10013
+ return group.rows.every(row => this.rowSucceeded(row));
10014
+ }
10015
+ rowSucceeded(row) {
10016
+ if (row.isFailed)
10017
+ return false;
10018
+ if (row.models.length)
10019
+ return row.models.some(this.succeeded);
10020
+ return row.subRows.length ? this.subRowsAllSucceeded(row) : true;
10021
+ }
10022
+ toggleGroup(group) {
10023
+ group.isOpen = !group.isOpen;
10024
+ !group.isOpen && group.rows.forEach(row => (row.isOpen = false));
10025
+ }
10026
+ toggleRow(row) {
10027
+ row.isOpen = !row.isOpen;
10028
+ }
10029
+ trackByGroup(_index, group) {
10030
+ return group.termId;
10031
+ }
10032
+ trackByRow(_index, row) {
10033
+ return `${row.termId}-${row.index}-${row.identityLabel}`;
10034
+ }
10035
+ trackByModel(_index, model) {
10036
+ return model.methodId;
10037
+ }
10038
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeJLogModelsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10039
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeJLogModelsComponent, isStandalone: true, selector: "he-node-jlog-models", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, originalValues: { classPropertyName: "originalValues", publicName: "originalValues", isSignal: true, isRequired: false, transformFunction: null }, recalculatedValues: { classPropertyName: "recalculatedValues", publicName: "recalculatedValues", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypes: { classPropertyName: "filterTermTypes", publicName: "filterTermTypes", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypesLabel: { classPropertyName: "filterTermTypesLabel", publicName: "filterTermTypesLabel", isSignal: true, isRequired: false, transformFunction: null }, cycle: { classPropertyName: "cycle", publicName: "cycle", isSignal: true, isRequired: false, transformFunction: null }, jlog: { classPropertyName: "jlog", publicName: "jlog", isSignal: true, isRequired: false, transformFunction: null }, jlogParentKey: { classPropertyName: "jlogParentKey", publicName: "jlogParentKey", isSignal: true, isRequired: false, transformFunction: null }, jlogParentIndex: { classPropertyName: "jlogParentIndex", publicName: "jlogParentIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\">\n @if (enableFilterByTerm()) {\n <div class=\"field is-pb-1\">\n <div class=\"control is-expanded has-icons-right\">\n <input\n class=\"input search-input is-small\"\n [ngModel]=\"term()\"\n name=\"term\"\n placeholder=\"Select entry by name\"\n [ngbTypeahead]=\"suggestTerm\"\n [resultFormatter]=\"termFormatter\"\n [inputFormatter]=\"termFormatter\"\n [focusFirst]=\"true\"\n (focus)=\"typeaheadFocus($event)\"\n (selectItem)=\"term.set($event.item)\"\n container=\"body\"\n popupClass=\"is-small\" />\n <a class=\"icon is-small is-right\" [class.is-hidden]=\"!term()\" (click)=\"term.set(undefined)\">\n <he-svg-icon name=\"xmark\" />\n </a>\n </div>\n </div>\n }\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\"><span>Units</span></th>\n }\n <th class=\"has-border-right\"><span>Original</span></th>\n <th class=\"has-border-right\"><span>Recalculated</span></th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\"><span>Difference</span></th>\n }\n @for (c of methodModelsCount() | times; track i; let i = $index) {\n <th class=\"has-border-right\"><span>Model {{ i + 1 }}</span></th>\n }\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">No recalculation logs to show.</p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n @if (group.term) {\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n } @else if (group.key) {\n @if (nodeKey() === 'completeness') {\n <a [href]=\"schemaBaseUrl + '/Completeness#' + group.key\" target=\"_blank\">\n <span>{{ group.key | keyToLabel }}</span>\n </a>\n } @else {\n <a [href]=\"schemaBaseUrl + '/' + nodeType() + '#' + group.key\" target=\"_blank\">\n <span>{{ group.key | keyToLabel }}</span>\n </a>\n }\n }\n </div>\n </td>\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0] }\" />\n <ng-container *ngTemplateOutlet=\"modelCells; context: { row: group.rows[0], open: group.isOpen }\" />\n } @else {\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if (group.term) {\n <span class=\"is-nowrap\" [innerHtml]=\"group.term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n @if (!isEmptyValue(group.originalValue)) {\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: group.originalValue }\" />\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (!isEmptyValue(group.recalculatedValue)) {\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: group.recalculatedValue }\" />\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right is-nowrap\">\n @if (!isEmptyValue(group.originalValue) && !isEmptyValue(group.recalculatedValue)) {\n <he-blank-node-value-delta\n [value]=\"group.recalculatedValue\"\n [originalValue]=\"group.originalValue\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n </td>\n }\n <td class=\"has-border-right\" [attr.colspan]=\"methodModelsCount()\">\n <span>Expand to see logs (</span>\n @if (groupAllSucceeded(group)) {\n <span>all succeeded</span>\n <he-svg-icon class=\"is-ml-1 has-text-success\" name=\"checkmark\" />\n } @else {\n <span>some failed</span>\n <he-svg-icon class=\"is-ml-1 has-text-danger\" name=\"xmark\" />\n }\n <span>)</span>\n </td>\n }\n </tr>\n\n @if (single) {\n <ng-container\n *ngTemplateOutlet=\"subRowsTemplate; context: { row: group.rows[0], open: group.isOpen, nested: false }\" />\n } @else if (group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr [class.has-sub-rows]=\"row.canOpen\" [class.is-sub-row]=\"true\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n @if (row.canOpen) {\n <a class=\"open-node\" (click)=\"toggleRow(row)\">\n <he-svg-icon [name]=\"row.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-gap-4\">\n @if (row.isFailed) {\n <span class=\"has-text-danger\">failed</span>\n } @else if (row.identitySegments.length || row.identityLabel) {\n @for (segment of row.identitySegments; track segment.field) {\n <span>{{ segment.field | keyToLabel }}:</span>\n @for (term of segment.terms; track term['@id']) {\n <he-node-link class=\"is-inline-block\" [node]=\"term\">\n <span class=\"break-word\" [innerHtml]=\"term.name | compound: term.termType\"></span>\n </he-node-link>\n }\n }\n @if (row.identityLabel) {\n <span class=\"break-word\">{{ row.identityLabel }}</span>\n }\n } @else {\n <span class=\"has-text-grey\">entry {{ row.index + 1 }}</span>\n }\n </div>\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row }\" />\n <ng-container *ngTemplateOutlet=\"modelCells; context: { row, open: row.isOpen }\" />\n </tr>\n <ng-container *ngTemplateOutlet=\"subRowsTemplate; context: { row, open: row.isOpen, nested: true }\" />\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<div class=\"is-size-7\">\n <div class=\"is-flex is-py-2 is-px-3 is-gap-16 | status-legend\">\n <div\n class=\"is-flex is-justify-content-center is-align-items-center is-align-content-center is-flex-wrap-wrap is-gap-8\">\n @for (status of LogStatus | keyvalue; track status.value) {\n @if (logIcon[status.value]) {\n <span class=\"is-flex is-align-items-center is-gap-8\">\n <he-svg-icon [name]=\"logIcon[status.value]\" size=\"20\" class=\"has-text-{{ logColor[status.value] }}\" />\n <span class=\"is-size-7\">{{ status.value | capitalize }}</span>\n </span>\n }\n }\n </div>\n\n @if (filteredType()) {\n <div class=\"field is-relative\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded is-secondary\"\n [(ngModel)]=\"onlyRequired\"\n [disabled]=\"!!term()\"\n id=\"onlyRequiredJLog\" />\n <label class=\"is-size-7\" for=\"onlyRequiredJLog\">\n <span>Only show {{ filteredType() }} included in the default HESTIA system boundary</span>\n </label>\n </div>\n }\n </div>\n</div>\n\n<ng-template #valueCells let-row=\"row\">\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if (row.term) {\n <span class=\"is-nowrap\" [innerHtml]=\"row.term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n @if (!isEmptyValue(row.originalValue)) {\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: row.originalValue }\" />\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (row.isRecalculated) {\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: row.recalculatedValue }\" />\n } @else if (row.key === 'backgroundData') {\n <!-- a container grouping the input's background-emission models; it has no value of its own -->\n -\n } @else if (row.isFailed || row.models.length || isEmptyValue(row.originalValue)) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right is-nowrap\">\n @if (row.isOriginal && row.isRecalculated) {\n <he-blank-node-value-delta\n [value]=\"row.recalculatedValue\"\n [originalValue]=\"row.originalValue\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n </td>\n }\n</ng-template>\n\n<ng-template #subRowsTemplate let-row=\"row\" let-open=\"open\" let-nested=\"nested\">\n @if (row.canOpen && open) {\n @for (subRow of row.subRows; track trackByRow($index, subRow)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div\n class=\"is-flex is-align-items-flex-start is-flex-wrap-wrap is-gap-4\"\n [class.is-pl-3]=\"!nested\"\n [class.is-pl-5]=\"nested\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n @if (subRow.key === 'backgroundData') {\n <span>Background Data</span>\n } @else if (subRow.term) {\n <span>{{ subRow.key | keyToLabel }}:</span>\n <he-node-link class=\"is-inline-block\" [node]=\"subRow.term\">\n <span class=\"break-word\" [innerHtml]=\"subRow.term?.name | compound\"></span>\n </he-node-link>\n } @else {\n <span>Field:</span>\n @if (subRow.type) {\n <a [href]=\"schemaBaseUrl + '/' + subRow.type + '#' + subRow.key\" target=\"_blank\">{{ subRow.key }}</a>\n } @else {\n <span>{{ subRow.key }}</span>\n }\n }\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: subRow }\" />\n <ng-container *ngTemplateOutlet=\"modelCells; context: { row: subRow, open: true }\" />\n </tr>\n }\n }\n</ng-template>\n\n<ng-template #valueContent let-value=\"value\">\n @if (isNumber(value)) {\n {{ value | precision: 3 | default: '-' }}\n } @else {\n {{ value | default: '-' }}\n }\n</ng-template>\n\n<ng-template #modelCells let-row=\"row\" let-open=\"open\">\n @if (row.canOpen && !open && row.models.length === 0) {\n <td class=\"has-border-right\">\n <span>Expand to see logs (</span>\n @if (subRowsAllSucceeded(row)) {\n <span>all succeeded</span>\n <he-svg-icon class=\"is-ml-1 has-text-success\" name=\"checkmark\" />\n } @else {\n <span>some failed</span>\n <he-svg-icon class=\"is-ml-1 has-text-danger\" name=\"xmark\" />\n }\n <span>)</span>\n </td>\n @for (v of methodModelsCount() - 1 | times; track empty; let empty = $index) {\n <td class=\"has-border-right\"></td>\n }\n } @else {\n @let columns = modelColumns(row);\n @for (i of methodModelsCount() | times; track modelIndex; let modelIndex = $index) {\n <td class=\"has-border-right\">\n @if (columns[modelIndex]; as column) {\n @if (isArray(column)) {\n <!-- parallel models: stacked in the same column, each with its own status -->\n @for (model of column; track model.methodId) {\n <ng-container *ngTemplateOutlet=\"modelCell; context: { model, row }\" />\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"modelCell; context: { model: column, row }\" />\n }\n } @else {\n -\n }\n </td>\n }\n }\n</ng-template>\n\n<ng-template #modelCell let-model=\"model\" let-row=\"row\">\n <div class=\"is-flex is-align-self-stretch is-justify-content-center is-align-items-center is-gap-8\">\n <div class=\"is-flex is-gap-4 is-flex-grow-1 is-align-items-center\">\n <span\n class=\"pl-1 has-text-{{ logColor[model.status] }}\"\n [class.trigger-popover]=\"hasLogs()\"\n [ngbPopover]=\"logStatusDetails\"\n [disablePopover]=\"!hasLogs()\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p1=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"$event.stopPropagation(); p1.isOpen() ? p1.close() : p1.open({ model, row })\">\n <he-svg-icon [name]=\"logIcon[model.status]\" />\n </span>\n\n <span class=\"is-flex is-flex-grow-1 is-gap-4\">\n <span class=\"is-nowrap is-capitalized\">{{ methodName(model) }}</span>\n @if (modelMethodTier(model); as methodTier) {\n <span class=\"is-nowrap\">[{{ methodTier }}]</span>\n }\n </span>\n </div>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (model.showLogs) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"logDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ model })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n }\n @if (model.model) {\n @if (model.showLogs) {\n <div class=\"vertical-divider\"></div>\n }\n <ng-container *ngTemplateOutlet=\"docsLink; context: { $implicit: model.model }\" />\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #logDetails let-model=\"model\">\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n</ng-template>\n\n<ng-template #logStatusDetails let-model=\"model\" let-row=\"row\">\n <he-node-logs-models-logs-status [nodeType]=\"nodeType()\" [model]=\"model\" [data]=\"$any(row)\" />\n</ng-template>\n\n<ng-template #docsLink let-model>\n @if (guideEnabled && model.guidePath) {\n <he-guide-overlay [pageId]=\"model.guidePath\" [width]=\"500\" />\n } @else {\n <a [href]=\"model.docPath || model.path\" target=\"_blank\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .status-legend{border:1px solid #dbe3ea;background:#f5f7f9}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: BlankNodeValueDeltaComponent, selector: "he-blank-node-value-delta", inputs: ["value", "originalValue", "displayType", "useCustomFunctions"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "directive", type: NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: NodeLogsModelsLogsComponent, selector: "he-node-logs-models-logs", inputs: ["logs"] }, { kind: "component", type: NodeLogsModelsLogsStatusComponent, selector: "he-node-logs-models-logs-status", inputs: ["nodeType", "model", "data"] }, { kind: "component", type: GuideOverlayComponent, selector: "he-guide-overlay", inputs: ["pageId", "width", "height", "positions"], outputs: ["widthChange", "heightChange"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: KeyToLabelPipe, name: "keyToLabel" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: TimesPipe, name: "times" }, { kind: "pipe", type: CapitalizePipe, name: "capitalize" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10040
+ }
10041
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeJLogModelsComponent, decorators: [{
10042
+ type: Component$1,
10043
+ args: [{ selector: 'he-node-jlog-models', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
10044
+ KeyValuePipe,
10045
+ NgTemplateOutlet,
10046
+ FormsModule,
10047
+ DataTableComponent,
10048
+ BlankNodeValueDeltaComponent,
10049
+ NodeLinkComponent,
10050
+ NgbTypeahead,
10051
+ NgbPopover,
10052
+ CompoundPipe,
10053
+ DefaultPipe,
10054
+ KeyToLabelPipe,
10055
+ PrecisionPipe,
10056
+ TimesPipe,
10057
+ CapitalizePipe,
10058
+ HESvgIconComponent,
10059
+ NodeLogsModelsLogsComponent,
10060
+ NodeLogsModelsLogsStatusComponent,
10061
+ GuideOverlayComponent
10062
+ ], template: "<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\">\n @if (enableFilterByTerm()) {\n <div class=\"field is-pb-1\">\n <div class=\"control is-expanded has-icons-right\">\n <input\n class=\"input search-input is-small\"\n [ngModel]=\"term()\"\n name=\"term\"\n placeholder=\"Select entry by name\"\n [ngbTypeahead]=\"suggestTerm\"\n [resultFormatter]=\"termFormatter\"\n [inputFormatter]=\"termFormatter\"\n [focusFirst]=\"true\"\n (focus)=\"typeaheadFocus($event)\"\n (selectItem)=\"term.set($event.item)\"\n container=\"body\"\n popupClass=\"is-small\" />\n <a class=\"icon is-small is-right\" [class.is-hidden]=\"!term()\" (click)=\"term.set(undefined)\">\n <he-svg-icon name=\"xmark\" />\n </a>\n </div>\n </div>\n }\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\"><span>Units</span></th>\n }\n <th class=\"has-border-right\"><span>Original</span></th>\n <th class=\"has-border-right\"><span>Recalculated</span></th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\"><span>Difference</span></th>\n }\n @for (c of methodModelsCount() | times; track i; let i = $index) {\n <th class=\"has-border-right\"><span>Model {{ i + 1 }}</span></th>\n }\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">No recalculation logs to show.</p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n @if (group.term) {\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n } @else if (group.key) {\n @if (nodeKey() === 'completeness') {\n <a [href]=\"schemaBaseUrl + '/Completeness#' + group.key\" target=\"_blank\">\n <span>{{ group.key | keyToLabel }}</span>\n </a>\n } @else {\n <a [href]=\"schemaBaseUrl + '/' + nodeType() + '#' + group.key\" target=\"_blank\">\n <span>{{ group.key | keyToLabel }}</span>\n </a>\n }\n }\n </div>\n </td>\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0] }\" />\n <ng-container *ngTemplateOutlet=\"modelCells; context: { row: group.rows[0], open: group.isOpen }\" />\n } @else {\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if (group.term) {\n <span class=\"is-nowrap\" [innerHtml]=\"group.term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n @if (!isEmptyValue(group.originalValue)) {\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: group.originalValue }\" />\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (!isEmptyValue(group.recalculatedValue)) {\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: group.recalculatedValue }\" />\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right is-nowrap\">\n @if (!isEmptyValue(group.originalValue) && !isEmptyValue(group.recalculatedValue)) {\n <he-blank-node-value-delta\n [value]=\"group.recalculatedValue\"\n [originalValue]=\"group.originalValue\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n </td>\n }\n <td class=\"has-border-right\" [attr.colspan]=\"methodModelsCount()\">\n <span>Expand to see logs (</span>\n @if (groupAllSucceeded(group)) {\n <span>all succeeded</span>\n <he-svg-icon class=\"is-ml-1 has-text-success\" name=\"checkmark\" />\n } @else {\n <span>some failed</span>\n <he-svg-icon class=\"is-ml-1 has-text-danger\" name=\"xmark\" />\n }\n <span>)</span>\n </td>\n }\n </tr>\n\n @if (single) {\n <ng-container\n *ngTemplateOutlet=\"subRowsTemplate; context: { row: group.rows[0], open: group.isOpen, nested: false }\" />\n } @else if (group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr [class.has-sub-rows]=\"row.canOpen\" [class.is-sub-row]=\"true\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n @if (row.canOpen) {\n <a class=\"open-node\" (click)=\"toggleRow(row)\">\n <he-svg-icon [name]=\"row.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-gap-4\">\n @if (row.isFailed) {\n <span class=\"has-text-danger\">failed</span>\n } @else if (row.identitySegments.length || row.identityLabel) {\n @for (segment of row.identitySegments; track segment.field) {\n <span>{{ segment.field | keyToLabel }}:</span>\n @for (term of segment.terms; track term['@id']) {\n <he-node-link class=\"is-inline-block\" [node]=\"term\">\n <span class=\"break-word\" [innerHtml]=\"term.name | compound: term.termType\"></span>\n </he-node-link>\n }\n }\n @if (row.identityLabel) {\n <span class=\"break-word\">{{ row.identityLabel }}</span>\n }\n } @else {\n <span class=\"has-text-grey\">entry {{ row.index + 1 }}</span>\n }\n </div>\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row }\" />\n <ng-container *ngTemplateOutlet=\"modelCells; context: { row, open: row.isOpen }\" />\n </tr>\n <ng-container *ngTemplateOutlet=\"subRowsTemplate; context: { row, open: row.isOpen, nested: true }\" />\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<div class=\"is-size-7\">\n <div class=\"is-flex is-py-2 is-px-3 is-gap-16 | status-legend\">\n <div\n class=\"is-flex is-justify-content-center is-align-items-center is-align-content-center is-flex-wrap-wrap is-gap-8\">\n @for (status of LogStatus | keyvalue; track status.value) {\n @if (logIcon[status.value]) {\n <span class=\"is-flex is-align-items-center is-gap-8\">\n <he-svg-icon [name]=\"logIcon[status.value]\" size=\"20\" class=\"has-text-{{ logColor[status.value] }}\" />\n <span class=\"is-size-7\">{{ status.value | capitalize }}</span>\n </span>\n }\n }\n </div>\n\n @if (filteredType()) {\n <div class=\"field is-relative\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded is-secondary\"\n [(ngModel)]=\"onlyRequired\"\n [disabled]=\"!!term()\"\n id=\"onlyRequiredJLog\" />\n <label class=\"is-size-7\" for=\"onlyRequiredJLog\">\n <span>Only show {{ filteredType() }} included in the default HESTIA system boundary</span>\n </label>\n </div>\n }\n </div>\n</div>\n\n<ng-template #valueCells let-row=\"row\">\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if (row.term) {\n <span class=\"is-nowrap\" [innerHtml]=\"row.term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n @if (!isEmptyValue(row.originalValue)) {\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: row.originalValue }\" />\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (row.isRecalculated) {\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: row.recalculatedValue }\" />\n } @else if (row.key === 'backgroundData') {\n <!-- a container grouping the input's background-emission models; it has no value of its own -->\n -\n } @else if (row.isFailed || row.models.length || isEmptyValue(row.originalValue)) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right is-nowrap\">\n @if (row.isOriginal && row.isRecalculated) {\n <he-blank-node-value-delta\n [value]=\"row.recalculatedValue\"\n [originalValue]=\"row.originalValue\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n </td>\n }\n</ng-template>\n\n<ng-template #subRowsTemplate let-row=\"row\" let-open=\"open\" let-nested=\"nested\">\n @if (row.canOpen && open) {\n @for (subRow of row.subRows; track trackByRow($index, subRow)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div\n class=\"is-flex is-align-items-flex-start is-flex-wrap-wrap is-gap-4\"\n [class.is-pl-3]=\"!nested\"\n [class.is-pl-5]=\"nested\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n @if (subRow.key === 'backgroundData') {\n <span>Background Data</span>\n } @else if (subRow.term) {\n <span>{{ subRow.key | keyToLabel }}:</span>\n <he-node-link class=\"is-inline-block\" [node]=\"subRow.term\">\n <span class=\"break-word\" [innerHtml]=\"subRow.term?.name | compound\"></span>\n </he-node-link>\n } @else {\n <span>Field:</span>\n @if (subRow.type) {\n <a [href]=\"schemaBaseUrl + '/' + subRow.type + '#' + subRow.key\" target=\"_blank\">{{ subRow.key }}</a>\n } @else {\n <span>{{ subRow.key }}</span>\n }\n }\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: subRow }\" />\n <ng-container *ngTemplateOutlet=\"modelCells; context: { row: subRow, open: true }\" />\n </tr>\n }\n }\n</ng-template>\n\n<ng-template #valueContent let-value=\"value\">\n @if (isNumber(value)) {\n {{ value | precision: 3 | default: '-' }}\n } @else {\n {{ value | default: '-' }}\n }\n</ng-template>\n\n<ng-template #modelCells let-row=\"row\" let-open=\"open\">\n @if (row.canOpen && !open && row.models.length === 0) {\n <td class=\"has-border-right\">\n <span>Expand to see logs (</span>\n @if (subRowsAllSucceeded(row)) {\n <span>all succeeded</span>\n <he-svg-icon class=\"is-ml-1 has-text-success\" name=\"checkmark\" />\n } @else {\n <span>some failed</span>\n <he-svg-icon class=\"is-ml-1 has-text-danger\" name=\"xmark\" />\n }\n <span>)</span>\n </td>\n @for (v of methodModelsCount() - 1 | times; track empty; let empty = $index) {\n <td class=\"has-border-right\"></td>\n }\n } @else {\n @let columns = modelColumns(row);\n @for (i of methodModelsCount() | times; track modelIndex; let modelIndex = $index) {\n <td class=\"has-border-right\">\n @if (columns[modelIndex]; as column) {\n @if (isArray(column)) {\n <!-- parallel models: stacked in the same column, each with its own status -->\n @for (model of column; track model.methodId) {\n <ng-container *ngTemplateOutlet=\"modelCell; context: { model, row }\" />\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"modelCell; context: { model: column, row }\" />\n }\n } @else {\n -\n }\n </td>\n }\n }\n</ng-template>\n\n<ng-template #modelCell let-model=\"model\" let-row=\"row\">\n <div class=\"is-flex is-align-self-stretch is-justify-content-center is-align-items-center is-gap-8\">\n <div class=\"is-flex is-gap-4 is-flex-grow-1 is-align-items-center\">\n <span\n class=\"pl-1 has-text-{{ logColor[model.status] }}\"\n [class.trigger-popover]=\"hasLogs()\"\n [ngbPopover]=\"logStatusDetails\"\n [disablePopover]=\"!hasLogs()\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p1=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"$event.stopPropagation(); p1.isOpen() ? p1.close() : p1.open({ model, row })\">\n <he-svg-icon [name]=\"logIcon[model.status]\" />\n </span>\n\n <span class=\"is-flex is-flex-grow-1 is-gap-4\">\n <span class=\"is-nowrap is-capitalized\">{{ methodName(model) }}</span>\n @if (modelMethodTier(model); as methodTier) {\n <span class=\"is-nowrap\">[{{ methodTier }}]</span>\n }\n </span>\n </div>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (model.showLogs) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"logDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ model })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n }\n @if (model.model) {\n @if (model.showLogs) {\n <div class=\"vertical-divider\"></div>\n }\n <ng-container *ngTemplateOutlet=\"docsLink; context: { $implicit: model.model }\" />\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #logDetails let-model=\"model\">\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n</ng-template>\n\n<ng-template #logStatusDetails let-model=\"model\" let-row=\"row\">\n <he-node-logs-models-logs-status [nodeType]=\"nodeType()\" [model]=\"model\" [data]=\"$any(row)\" />\n</ng-template>\n\n<ng-template #docsLink let-model>\n @if (guideEnabled && model.guidePath) {\n <he-guide-overlay [pageId]=\"model.guidePath\" [width]=\"500\" />\n } @else {\n <a [href]=\"model.docPath || model.path\" target=\"_blank\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .status-legend{border:1px solid #dbe3ea;background:#f5f7f9}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}\n"] }]
10063
+ }], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }], nodeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKey", required: false }] }], originalValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "originalValues", required: false }] }], recalculatedValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "recalculatedValues", required: false }] }], filterTermTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterTermTypes", required: false }] }], filterTermTypesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterTermTypesLabel", required: false }] }], cycle: [{ type: i0.Input, args: [{ isSignal: true, alias: "cycle", required: false }] }], jlog: [{ type: i0.Input, args: [{ isSignal: true, alias: "jlog", required: false }] }], jlogParentKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "jlogParentKey", required: false }] }], jlogParentIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "jlogParentIndex", required: false }] }] } });
10064
+
9164
10065
  const groupTerms = (terms) => terms.reduce((prev, curr) => ({ ...prev, [curr['@id']]: curr }), {});
9165
10066
  const logIcon = {
9166
10067
  [LogStatus.success]: 'checkmark',
@@ -9267,6 +10168,9 @@ class NodeLogsModelsComponent {
9267
10168
  this.logsKey = input('', ...(ngDevMode ? [{ debugName: "logsKey" }] : []));
9268
10169
  this.noDataMessage = input('', ...(ngDevMode ? [{ debugName: "noDataMessage" }] : []));
9269
10170
  this.cycle = input(...(ngDevMode ? [undefined, { debugName: "cycle" }] : []));
10171
+ // for a grouped sub-node view (e.g. an animal's inputs), scope the `.jlog` to its parent entry
10172
+ this.jlogParentKey = input(...(ngDevMode ? [undefined, { debugName: "jlogParentKey" }] : []));
10173
+ this.jlogParentIndex = input(...(ngDevMode ? [undefined, { debugName: "jlogParentIndex" }] : []));
9270
10174
  this.isEmpty = isEmpty;
9271
10175
  this.schemaBaseUrl = schemaBaseUrl();
9272
10176
  this.isExternal = isExternal();
@@ -9291,12 +10195,27 @@ class NodeLogsModelsComponent {
9291
10195
  this.hasContributions = computed(() => [this.nodeType() === NodeType.ImpactAssessment, ['impacts', 'endpoints'].includes(this.nodeKey())].every(Boolean), ...(ngDevMode ? [{ debugName: "hasContributions" }] : []));
9292
10196
  this.logsUrl = computed(() => this.nodeService.nodeLogsUrl(this.node()), ...(ngDevMode ? [{ debugName: "logsUrl" }] : []));
9293
10197
  this.nodeType = computed(() => nodeType(this.node()), ...(ngDevMode ? [{ debugName: "nodeType" }] : []));
10198
+ // the new `.jlog` (JSON) recalculation logs, when available
10199
+ this.jlogResource = rxResource({
10200
+ params: () => ({ node: this.node() }),
10201
+ stream: ({ params: { node } }) => this.nodeLogsModelsService.getJLog$(node)
10202
+ });
10203
+ this.jlog = computed(() => this.jlogResource.value() ?? {}, ...(ngDevMode ? [{ debugName: "jlog" }] : []));
10204
+ this.loadingJLog = computed(() => !this.jlogResource.hasValue() || this.jlogResource.isLoading(), ...(ngDevMode ? [{ debugName: "loadingJLog" }] : []));
10205
+ // a `.jlog` means a new calculation (no text logs): render everything with the dedicated component.
10206
+ // the legacy text path only remains for older calculations that have no `.jlog`.
10207
+ this.useJLog = computed(() => !isEmpty(this.jlog()), ...(ngDevMode ? [{ debugName: "useJLog" }] : []));
10208
+ // only fetch the legacy text logs once we know the node has no `.jlog`
9294
10209
  this.logsResource = rxResource({
9295
- params: () => ({ node: this.node(), groupByAnimal: !!this.logsKey() }),
9296
- stream: ({ params: { node, groupByAnimal } }) => this.nodeLogsModelsService.getNodeLogs$(node, groupByAnimal)
10210
+ params: () => ({
10211
+ node: this.node(),
10212
+ groupByAnimal: !!this.logsKey(),
10213
+ skip: this.loadingJLog() || this.useJLog()
10214
+ }),
10215
+ stream: ({ params: { node, groupByAnimal, skip } }) => skip ? of({}) : this.nodeLogsModelsService.getNodeLogs$(node, groupByAnimal)
9297
10216
  });
9298
10217
  this.allLogs = computed(() => this.logsResource.value() ?? {}, ...(ngDevMode ? [{ debugName: "allLogs" }] : []));
9299
- this.hasLogs = computed(() => !isEmpty(this.allLogs()), ...(ngDevMode ? [{ debugName: "hasLogs" }] : []));
10218
+ this.hasLogs = computed(() => !isEmpty(this.allLogs()) || this.useJLog(), ...(ngDevMode ? [{ debugName: "hasLogs" }] : []));
9300
10219
  this.configResource = rxResource({
9301
10220
  params: () => ({ node: this.node() }),
9302
10221
  stream: ({ params: { node } }) => this.engineService.ochestratorConfig$(nodeType(node), nodeId(node))
@@ -9463,7 +10382,7 @@ class NodeLogsModelsComponent {
9463
10382
  return subValues.every(v => v.configModels.some(vv => [LogStatus.success, LogStatus.notRequired, LogStatus.dataProvided].includes(vv.status)));
9464
10383
  }
9465
10384
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeLogsModelsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9466
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeLogsModelsComponent, isStandalone: true, selector: "he-node-logs-models", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, originalValues: { classPropertyName: "originalValues", publicName: "originalValues", isSignal: true, isRequired: false, transformFunction: null }, recalculatedValues: { classPropertyName: "recalculatedValues", publicName: "recalculatedValues", isSignal: true, isRequired: false, transformFunction: null }, terms: { classPropertyName: "terms", publicName: "terms", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypes: { classPropertyName: "filterTermTypes", publicName: "filterTermTypes", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypesLabel: { classPropertyName: "filterTermTypesLabel", publicName: "filterTermTypesLabel", isSignal: true, isRequired: false, transformFunction: null }, logsKey: { classPropertyName: "logsKey", publicName: "logsKey", isSignal: true, isRequired: false, transformFunction: null }, noDataMessage: { classPropertyName: "noDataMessage", publicName: "noDataMessage", isSignal: true, isRequired: false, transformFunction: null }, cycle: { classPropertyName: "cycle", publicName: "cycle", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n\n @if (!isExternal && !loading() && logsUrl() && hasLogs()) {\n <a class=\"is-size-7\" [href]=\"logsUrl()\" target=\"_blank\">\n <span>Open Full Logs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</div>\n\n@if (!isExternal && !loading() && !hasLogs()) {\n <p class=\"is-my-2\">\n <he-svg-icon class=\"has-text-warning\" name=\"exclamation-triangle\" />\n <span class=\"is-size-7 has-text-warning is-pl-1\">No logs found. Recalculation logs will be incomplete.</span>\n </p>\n}\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\">\n @if (enableFilterByTerm()) {\n <div class=\"field is-pb-1\">\n <div class=\"control is-expanded has-icons-right\">\n <input\n class=\"input search-input is-small\"\n [ngModel]=\"term()\"\n name=\"term\"\n placeholder=\"Select entry by name\"\n [ngbTypeahead]=\"suggestTerm\"\n [resultFormatter]=\"termFormatter\"\n [inputFormatter]=\"termFormatter\"\n [focusFirst]=\"true\"\n (focus)=\"typeaheadFocus($event)\"\n (selectItem)=\"term.set($event.item)\"\n container=\"body\"\n popupClass=\"is-small\" />\n <a class=\"icon is-small is-right\" [class.is-hidden]=\"!term()\" (click)=\"term.set(undefined)\">\n <he-svg-icon name=\"xmark\" />\n </a>\n </div>\n </div>\n }\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>\n <span class=\"is-pr-1\">Units</span>\n @if (functionalUnit()) {\n <span>(per&nbsp;</span>\n <span>{{ functionalUnit() }}</span>\n <span>)</span>\n }\n </span>\n </div>\n </th>\n }\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Original</span>\n </div>\n </th>\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Recalculated</span>\n </div>\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Difference</span>\n </div>\n </th>\n }\n @for (c of methodModelsCount() | times; track i; let i = $index) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Model {{ i + 1 }}</span>\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (loading()) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <div class=\"has-text-center py-3\">\n <he-svg-icon name=\"loading\" animation=\"spin\" size=\"40\" />\n </div>\n </td>\n </tr>\n } @else if (blankNodes().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">\n @if (noDataMessage()) {\n <span>{{ noDataMessage() }}</span>\n } @else {\n <ng-container *ngTemplateOutlet=\"noResultsDefaultMessage\" />\n }\n </p>\n </td>\n </tr>\n }\n @for (blankNode of blankNodes(); track trackByBlankNode($index, blankNode)) {\n @let configModels = $any(blankNode).configModels;\n <tr [class.has-sub-rows]=\"blankNode.canOpen\" [class.is-open]=\"blankNode.isOpen\">\n <td\n class=\"width-auto has-border-right is-nowrap\"\n [attr.title]=\"$any(blankNode).term?.name || $any(blankNode).key\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n <ng-container *ngTemplateOutlet=\"collapseButton; context: { $implicit: blankNode }\" />\n @if ($any(blankNode).term) {\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"$any(blankNode).term\">\n <span\n class=\"break-word\"\n [innerHtml]=\"$any(blankNode).term.name | compound: $any(blankNode).term.termType\"></span>\n </he-node-link>\n } @else if ($any(blankNode).key) {\n @if (nodeKey() === 'completeness') {\n <a [href]=\"schemaBaseUrl + '/Completeness#' + $any(blankNode).key\" target=\"_blank\">\n <span>{{ $any(blankNode).key | keyToLabel }}</span>\n </a>\n } @else {\n <a [href]=\"schemaBaseUrl + '/' + nodeType() + '#' + $any(blankNode).key\" target=\"_blank\">\n <span>{{ $any(blankNode).key | keyToLabel }}</span>\n </a>\n }\n }\n </div>\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if ($any(blankNode).term) {\n <span class=\"is-nowrap\" [innerHtml]=\"$any(blankNode).term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n <ng-template #originalValueContent>\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: $any(blankNode).originalValue }\" />\n </span>\n </ng-template>\n\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n <ng-container\n *ngTemplateOutlet=\"\n valueContent;\n context: { value: $any(blankNode).originalValueByMethodId[model.methodId] }\n \" />\n </div>\n }\n } @else {\n <span\n [class.trigger-popover]=\"!!$any(blankNode).original?.[0]?.methodModel\"\n [ngbPopover]=\"blankNodeOriginalValueDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n placement=\"bottom left right auto\"\n container=\"body\"\n [disablePopover]=\"!$any(blankNode).original?.[0]?.methodModel\"\n [popoverContext]=\"{ blankNode }\">\n <span pointer>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: $any(blankNode).originalValue }\" />\n </span>\n </span>\n }\n </td>\n <td class=\"has-border-right\">\n @if (blankNode.isRecalculated) {\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n <ng-container\n *ngTemplateOutlet=\"\n valueContent;\n context: { value: $any(blankNode).recalculatedValueByMethodId[model.methodId] }\n \" />\n </div>\n }\n } @else {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: blankNode.recalculatedValue }\" />\n </span>\n }\n } @else if (configModels?.length) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right is-nowrap\">\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n @if (\n $any(blankNode).originalValueByMethodId[model.methodId] !== null &&\n $any(blankNode).recalculatedValueByMethodId[model.methodId] !== null\n ) {\n <he-blank-node-value-delta\n [value]=\"$any(blankNode).recalculatedValueByMethodId[model.methodId]\"\n [originalValue]=\"$any(blankNode).originalValueByMethodId[model.methodId]\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n </div>\n }\n } @else {\n @if ($any(blankNode).original.length && blankNode.isRecalculated) {\n <he-blank-node-value-delta\n [value]=\"blankNode.recalculatedValue\"\n [originalValue]=\"$any(blankNode).originalValue\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n }\n </td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: blankNode }\" />\n </tr>\n @for (subValue of $any(blankNode).keys; track trackBySubValue(subValue)) {\n @if (blankNode.isOpen) {\n <tr [class.has-sub-rows]=\"$any(blankNode).subValues?.length\" [class.is-sub-row]=\"blankNode.canOpen\">\n <td class=\"width-auto has-border-right is-nowrap\">\n <span class=\"is-inline-block is-align-top pl-3 pr-1 field-node\">Field:</span>\n @if (blankNode.type) {\n <a\n class=\"is-inline-block is-pre-wrap\"\n [href]=\"schemaBaseUrl + '/' + blankNode.type + '#' + subValue.key\"\n target=\"_blank\"\n [title]=\"subValue.key\">\n <span>{{ subValue.key }}</span>\n </a>\n }\n @if (!blankNode.type) {\n <span class=\"is-inline-block is-align-top\">{{ subValue.key }}</span>\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\"></td>\n }\n <td class=\"has-border-right\">\n @if (subValue.originalValue !== null) {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.originalValue }\" />\n </span>\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (subValue.isRecalculated || subValue.key === 'impactAssessment') {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n } @else {\n not recalculated\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">-</td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: subValue }\" />\n </tr>\n }\n }\n @for (subValue of $any(blankNode).subValues; track trackBySubValue(subValue)) {\n <ng-container *ngTemplateOutlet=\"subValueRow; context: { blankNode, parent: blankNode, subValue }\" />\n }\n }\n </tbody>\n </table>\n</he-data-table>\n<div class=\"is-size-7\">\n <div class=\"is-flex is-py-2 is-px-3 is-gap-16 | status-legend\">\n <div\n class=\"is-flex is-justify-content-center is-align-items-center is-align-content-center is-flex-wrap-wrap is-gap-8\">\n @for (status of LogStatus | keyvalue; track status.value) {\n @if (logIcon[status.value]) {\n <span class=\"is-flex is-align-items-center is-gap-8\">\n <he-svg-icon [name]=\"logIcon[status.value]\" size=\"20\" class=\"has-text-{{ logColor[status.value] }}\" />\n <span class=\"is-size-7\">{{ status.value | capitalize }}</span>\n </span>\n }\n }\n </div>\n\n @if (filteredType()) {\n <div class=\"field is-relative\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded is-secondary\"\n [(ngModel)]=\"onlyRequired\"\n [disabled]=\"!!term()\"\n id=\"onlyRequired\" />\n <label class=\"is-size-7\" for=\"onlyRequired\">\n <span>Only show {{ filteredType() }} included in the default HESTIA system boundary</span>\n </label>\n </div>\n }\n </div>\n</div>\n\n<ng-template #noResultsDefaultMessage>\n <span>No original data was provided and no gap filling occurred</span>\n @if (term() && !isInSystemBoundary(term()['@id'])) {\n <span class=\"is-pl-1\">as</span>\n <i class=\"is-px-1\">{{ term().name }}</i>\n <span>is not in the HESTIA system boundary</span>\n }\n <span>.</span>\n</ng-template>\n\n<ng-template #valueContent let-value=\"value\">\n @if (isNumber(value)) {\n {{ value | precision: 3 | default: '-' }}\n } @else {\n {{ value | default: '-' }}\n }\n</ng-template>\n\n<ng-template #collapseButton let-blankNode>\n @if (blankNode.canOpen) {\n <a class=\"open-node\" (click)=\"toggleBlankNode(blankNode)\">\n <he-svg-icon [name]=\"blankNode.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n</ng-template>\n\n<ng-template #subValueRow let-blankNode=\"blankNode\" let-parent=\"parent\" let-subValue=\"subValue\" let-rowClass=\"rowClass\">\n @let term = subValue.term || termById(subValue.id);\n @if (parent.isOpen) {\n <tr [class.is-sub-row]=\"parent.canOpen\" [ngClass]=\"rowClass\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 h-100\">\n <ng-container *ngTemplateOutlet=\"collapseButton; context: { $implicit: subValue }\" />\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <div class=\"is-flex is-align-items-flex-start is-flex-wrap-wrap is-gap-4\" [class.is-pl-3]=\"!subValue.canOpen\">\n <span>\n <span>{{ subValue.key | keyToLabel }}</span>\n @if (subValue.id) {\n <span class=\"is-inline-block\">:</span>\n }\n </span>\n @if (subValue.id) {\n @switch (subValue.key) {\n @case ('backgroundData') {\n <span class=\"is-inline-block\">{{ term?.name }}</span>\n }\n @case ('animal') {\n <span class=\"is-inline-block\">{{ subValue.id }}</span>\n }\n @default {\n <he-node-link\n class=\"is-inline-block\"\n linkClass=\"is-inline-block\"\n [node]=\"term\"\n [attr.title]=\"term?.name\">\n <span class=\"break-word\" [innerHtml]=\"term?.name | compound\"></span>\n </he-node-link>\n }\n }\n }\n </div>\n </div>\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if (subValue.showUnits && $any(blankNode).term) {\n <span class=\"is-nowrap\" [innerHtml]=\"$any(blankNode).term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n @if (!isEmpty(subValue.originalValue)) {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.originalValue }\" />\n </span>\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (subValue.isRecalculated) {\n @if (subValue.multiGroups) {\n <span\n class=\"trigger-popover\"\n ngbPopover=\"The total value across all inputs\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\">\n <span pointer>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n </span>\n } @else {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n }\n } @else if (!isEmpty(subValue.originalValue)) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">-</td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: subValue }\" />\n </tr>\n }\n @if (subValue.subValues) {\n @for (sub of subValue.subValues; track trackBySubValue(sub)) {\n <ng-container\n *ngTemplateOutlet=\"\n subValueRow;\n context: { blankNode, parent: subValue, subValue: sub, rowClass: 'is-sub-sub-row' }\n \" />\n }\n }\n</ng-template>\n\n<ng-template #blankNodeOriginalValueDetails let-blankNode=\"blankNode\">\n <span class=\"is-pr-1\">The original value was reported using:</span>\n <he-node-link\n class=\"is-inline-block\"\n linkClass=\"is-dark\"\n [node]=\"$any(blankNode).original[0].methodModel\"\n [showExternalLink]=\"true\">\n <span>{{ $any(blankNode).original[0].methodModel.name }}</span>\n </he-node-link>\n</ng-template>\n\n<ng-template #blankNodeModels let-data=\"data\">\n @let extraColumns = methodModelsCount() - 1;\n\n <ng-template #notInSystemBoundary>\n <td class=\"has-border-right\">\n <span>Not in HESTIA system boundary</span>\n </td>\n @for (v of data | repeat: extraColumns; track repeatIndex; let repeatIndex = $index) {\n <td class=\"has-border-right\"></td>\n }\n </ng-template>\n\n @if (data.canOpen && !data.isOpen && !data.configModels?.length) {\n <td class=\"has-border-right\">\n <span>Expand to see logs (</span>\n @let key = subValuesKey(data, 'sub-values');\n @if (hasCompleteSuccess(data)) {\n <span>all succeeded</span>\n <he-svg-icon class=\"is-ml-1\" name=\"checkmark\" class=\"has-text-success\" />\n } @else {\n <span>some failed</span>\n <he-svg-icon class=\"is-ml-1\" name=\"xmark\" class=\"has-text-danger\" />\n }\n <span>)</span>\n </td>\n @for (v of data | repeat: extraColumns; track repeatIndex; let repeatIndex = $index) {\n <td class=\"has-border-right\"></td>\n }\n } @else {\n @for (configModel of methodModelsCount() | times; track configModelIndex; let configModelIndex = $index) {\n <td class=\"has-border-right blank-node-index-{{ configModelIndex }}\">\n @if (getModelsAt(data, configModelIndex); as models) {\n @if ($any(models) | isArray) {\n <div>\n @for (model of $any(models); track model.methodId) {\n <p>\n <ng-container *ngTemplateOutlet=\"blankNodeModel; context: { model, data }\" />\n </p>\n }\n </div>\n } @else {\n <ng-container *ngTemplateOutlet=\"blankNodeModel; context: { model: models, data }\" />\n }\n } @else {\n -\n }\n </td>\n }\n }\n</ng-template>\n\n<ng-template #blankNodeModel let-model=\"model\" let-data=\"data\">\n <div class=\"is-flex is-align-self-stretch is-justify-content-center is-align-items-center is-gap-8\">\n <div class=\"is-flex is-gap-4 is-flex-grow-1 is-align-items-center\">\n <span\n class=\"pl-1 has-text-{{ logColor[model.status] }}\"\n [class.trigger-popover]=\"hasLogs()\"\n [ngbPopover]=\"logStatusDetails\"\n [disablePopover]=\"!hasLogs()\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p1=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"$event.stopPropagation(); p1.isOpen() ? p1.close() : p1.open({ model, data })\">\n <he-svg-icon [name]=\"logIcon[model.status]\" />\n </span>\n\n <span class=\"is-flex is-flex-grow-1 is-gap-4\">\n <span class=\"is-nowrap is-capitalized\">{{ methodName(model) }}</span>\n\n @if (model.logs?.methodTier || model.model?.methodTier) {\n <span class=\"is-nowrap\">[{{ model.logs?.methodTier || model.model?.methodTier }}]</span>\n }\n </span>\n </div>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (model.showLogs) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"logDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : model.showLogs ? p.open({ model }) : null\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n }\n\n @if (model.model) {\n @if (model.showLogs) {\n <div class=\"vertical-divider\"></div>\n }\n <ng-container *ngTemplateOutlet=\"docsLink; context: { $implicit: model.model }\" />\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #logDetails let-model=\"model\">\n @if (hasContributions()) {\n @if (showContributionsLogs()) {\n <div class=\"has-text-right is-mb-1\">\n <button class=\"button is-white is-small is-outlined\" (click)=\"showContributionsLogs.set(false)\">\n Hide contributions details\n </button>\n </div>\n <he-node-logs-models-contributions [node]=\"node()\" [nodeKey]=\"nodeKey()\" [model]=\"model\" />\n } @else {\n <div class=\"has-text-right is-mb-1\">\n <button class=\"button is-white is-small is-outlined\" (click)=\"showContributionsLogs.set(true)\">\n Show contributions details\n </button>\n </div>\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n }\n } @else {\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n }\n</ng-template>\n\n<ng-template #logStatusDetails let-model=\"model\" let-data=\"data\">\n <he-node-logs-models-logs-status [nodeType]=\"nodeType()\" [model]=\"model\" [data]=\"\" />\n</ng-template>\n\n<ng-template #docsLink let-model>\n @if (guideEnabled && model.guidePath) {\n <he-guide-overlay [pageId]=\"model.guidePath\" [width]=\"500\" />\n } @else {\n <a [href]=\"model.docPath || model.path\" target=\"_blank\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .status-legend{border:1px solid #dbe3ea;background:#f5f7f9}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row td:first-child{padding-left:12px}::ng-deep .table .is-sub-row .sub-sub-row-icon{display:none}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table .is-sub-sub-row .sub-sub-row-icon{display:inline-block}::ng-deep .table .is-sub-sub-row .sub-sub-row-icon+div{padding-left:0!important}::ng-deep .table .popover-body .table-container{max-height:260px;overflow-y:auto}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeValueDeltaComponent, selector: "he-blank-node-value-delta", inputs: ["value", "originalValue", "displayType", "useCustomFunctions"] }, { kind: "directive", type: NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: NodeLogsModelsLogsComponent, selector: "he-node-logs-models-logs", inputs: ["logs"] }, { kind: "component", type: NodeLogsModelsLogsStatusComponent, selector: "he-node-logs-models-logs-status", inputs: ["nodeType", "model", "data"] }, { kind: "component", type: NodeLogsModelsContributionsComponent, selector: "he-node-logs-models-contributions", inputs: ["node", "nodeKey", "model"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: GuideOverlayComponent, selector: "he-guide-overlay", inputs: ["pageId", "width", "height", "positions"], outputs: ["widthChange", "heightChange"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: KeyToLabelPipe, name: "keyToLabel" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: TimesPipe, name: "times" }, { kind: "pipe", type: IsArrayPipe, name: "isArray" }, { kind: "pipe", type: RepeatPipe, name: "repeat" }, { kind: "pipe", type: CapitalizePipe, name: "capitalize" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10385
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeLogsModelsComponent, isStandalone: true, selector: "he-node-logs-models", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, originalValues: { classPropertyName: "originalValues", publicName: "originalValues", isSignal: true, isRequired: false, transformFunction: null }, recalculatedValues: { classPropertyName: "recalculatedValues", publicName: "recalculatedValues", isSignal: true, isRequired: false, transformFunction: null }, terms: { classPropertyName: "terms", publicName: "terms", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypes: { classPropertyName: "filterTermTypes", publicName: "filterTermTypes", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypesLabel: { classPropertyName: "filterTermTypesLabel", publicName: "filterTermTypesLabel", isSignal: true, isRequired: false, transformFunction: null }, logsKey: { classPropertyName: "logsKey", publicName: "logsKey", isSignal: true, isRequired: false, transformFunction: null }, noDataMessage: { classPropertyName: "noDataMessage", publicName: "noDataMessage", isSignal: true, isRequired: false, transformFunction: null }, cycle: { classPropertyName: "cycle", publicName: "cycle", isSignal: true, isRequired: false, transformFunction: null }, jlogParentKey: { classPropertyName: "jlogParentKey", publicName: "jlogParentKey", isSignal: true, isRequired: false, transformFunction: null }, jlogParentIndex: { classPropertyName: "jlogParentIndex", publicName: "jlogParentIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n\n @if (!isExternal && !loading() && logsUrl() && hasLogs()) {\n <a class=\"is-size-7\" [href]=\"logsUrl()\" target=\"_blank\">\n <span>Open Full Logs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</div>\n\n@if (!isExternal && !loading() && !loadingJLog() && !hasLogs()) {\n <p class=\"is-my-2\">\n <he-svg-icon class=\"has-text-warning\" name=\"exclamation-triangle\" />\n <span class=\"is-size-7 has-text-warning is-pl-1\">No logs found. Recalculation logs will be incomplete.</span>\n </p>\n}\n\n@if (loadingJLog()) {\n <p class=\"has-text-centered py-3\">\n <he-svg-icon name=\"loading\" animation=\"spin\" size=\"40\" />\n </p>\n} @else if (useJLog()) {\n <he-node-jlog-models\n [node]=\"node()\"\n [nodeKey]=\"$any(nodeKey())\"\n [originalValues]=\"$any(originalValues())\"\n [recalculatedValues]=\"$any(recalculatedValues())\"\n [filterTermTypes]=\"filterTermTypes()\"\n [filterTermTypesLabel]=\"filterTermTypesLabel()\"\n [cycle]=\"cycle()\"\n [jlog]=\"jlog()\"\n [jlogParentKey]=\"jlogParentKey()\"\n [jlogParentIndex]=\"jlogParentIndex()\" />\n} @else {\n <he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\">\n @if (enableFilterByTerm()) {\n <div class=\"field is-pb-1\">\n <div class=\"control is-expanded has-icons-right\">\n <input\n class=\"input search-input is-small\"\n [ngModel]=\"term()\"\n name=\"term\"\n placeholder=\"Select entry by name\"\n [ngbTypeahead]=\"suggestTerm\"\n [resultFormatter]=\"termFormatter\"\n [inputFormatter]=\"termFormatter\"\n [focusFirst]=\"true\"\n (focus)=\"typeaheadFocus($event)\"\n (selectItem)=\"term.set($event.item)\"\n container=\"body\"\n popupClass=\"is-small\" />\n <a class=\"icon is-small is-right\" [class.is-hidden]=\"!term()\" (click)=\"term.set(undefined)\">\n <he-svg-icon name=\"xmark\" />\n </a>\n </div>\n </div>\n }\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>\n <span class=\"is-pr-1\">Units</span>\n @if (functionalUnit()) {\n <span>(per&nbsp;</span>\n <span>{{ functionalUnit() }}</span>\n <span>)</span>\n }\n </span>\n </div>\n </th>\n }\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Original</span>\n </div>\n </th>\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Recalculated</span>\n </div>\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Difference</span>\n </div>\n </th>\n }\n @for (c of methodModelsCount() | times; track i; let i = $index) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Model {{ i + 1 }}</span>\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (loading()) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <div class=\"has-text-center py-3\">\n <he-svg-icon name=\"loading\" animation=\"spin\" size=\"40\" />\n </div>\n </td>\n </tr>\n } @else if (blankNodes().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">\n @if (noDataMessage()) {\n <span>{{ noDataMessage() }}</span>\n } @else {\n <ng-container *ngTemplateOutlet=\"noResultsDefaultMessage\" />\n }\n </p>\n </td>\n </tr>\n }\n @for (blankNode of blankNodes(); track trackByBlankNode($index, blankNode)) {\n @let configModels = $any(blankNode).configModels;\n <tr [class.has-sub-rows]=\"blankNode.canOpen\" [class.is-open]=\"blankNode.isOpen\">\n <td\n class=\"width-auto has-border-right is-nowrap\"\n [attr.title]=\"$any(blankNode).term?.name || $any(blankNode).key\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n <ng-container *ngTemplateOutlet=\"collapseButton; context: { $implicit: blankNode }\" />\n @if ($any(blankNode).term) {\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"$any(blankNode).term\">\n <span\n class=\"break-word\"\n [innerHtml]=\"$any(blankNode).term.name | compound: $any(blankNode).term.termType\"></span>\n </he-node-link>\n } @else if ($any(blankNode).key) {\n @if (nodeKey() === 'completeness') {\n <a [href]=\"schemaBaseUrl + '/Completeness#' + $any(blankNode).key\" target=\"_blank\">\n <span>{{ $any(blankNode).key | keyToLabel }}</span>\n </a>\n } @else {\n <a [href]=\"schemaBaseUrl + '/' + nodeType() + '#' + $any(blankNode).key\" target=\"_blank\">\n <span>{{ $any(blankNode).key | keyToLabel }}</span>\n </a>\n }\n }\n </div>\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if ($any(blankNode).term) {\n <span class=\"is-nowrap\" [innerHtml]=\"$any(blankNode).term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n <ng-template #originalValueContent>\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: $any(blankNode).originalValue }\" />\n </span>\n </ng-template>\n\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n <ng-container\n *ngTemplateOutlet=\"\n valueContent;\n context: { value: $any(blankNode).originalValueByMethodId[model.methodId] }\n \" />\n </div>\n }\n } @else {\n <span\n [class.trigger-popover]=\"!!$any(blankNode).original?.[0]?.methodModel\"\n [ngbPopover]=\"blankNodeOriginalValueDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n placement=\"bottom left right auto\"\n container=\"body\"\n [disablePopover]=\"!$any(blankNode).original?.[0]?.methodModel\"\n [popoverContext]=\"{ blankNode }\">\n <span pointer>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: $any(blankNode).originalValue }\" />\n </span>\n </span>\n }\n </td>\n <td class=\"has-border-right\">\n @if (blankNode.isRecalculated) {\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n <ng-container\n *ngTemplateOutlet=\"\n valueContent;\n context: { value: $any(blankNode).recalculatedValueByMethodId[model.methodId] }\n \" />\n </div>\n }\n } @else {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: blankNode.recalculatedValue }\" />\n </span>\n }\n } @else if (configModels?.length) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right is-nowrap\">\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n @if (\n $any(blankNode).originalValueByMethodId[model.methodId] !== null &&\n $any(blankNode).recalculatedValueByMethodId[model.methodId] !== null\n ) {\n <he-blank-node-value-delta\n [value]=\"$any(blankNode).recalculatedValueByMethodId[model.methodId]\"\n [originalValue]=\"$any(blankNode).originalValueByMethodId[model.methodId]\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n </div>\n }\n } @else {\n @if ($any(blankNode).original.length && blankNode.isRecalculated) {\n <he-blank-node-value-delta\n [value]=\"blankNode.recalculatedValue\"\n [originalValue]=\"$any(blankNode).originalValue\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n }\n </td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: blankNode }\" />\n </tr>\n @for (subValue of $any(blankNode).keys; track trackBySubValue(subValue)) {\n @if (blankNode.isOpen) {\n <tr [class.has-sub-rows]=\"$any(blankNode).subValues?.length\" [class.is-sub-row]=\"blankNode.canOpen\">\n <td class=\"width-auto has-border-right is-nowrap\">\n <span class=\"is-inline-block is-align-top pl-3 pr-1 field-node\">Field:</span>\n @if (blankNode.type) {\n <a\n class=\"is-inline-block is-pre-wrap\"\n [href]=\"schemaBaseUrl + '/' + blankNode.type + '#' + subValue.key\"\n target=\"_blank\"\n [title]=\"subValue.key\">\n <span>{{ subValue.key }}</span>\n </a>\n }\n @if (!blankNode.type) {\n <span class=\"is-inline-block is-align-top\">{{ subValue.key }}</span>\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\"></td>\n }\n <td class=\"has-border-right\">\n @if (subValue.originalValue !== null) {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.originalValue }\" />\n </span>\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (subValue.isRecalculated || subValue.key === 'impactAssessment') {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n } @else {\n not recalculated\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">-</td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: subValue }\" />\n </tr>\n }\n }\n @for (subValue of $any(blankNode).subValues; track trackBySubValue(subValue)) {\n <ng-container *ngTemplateOutlet=\"subValueRow; context: { blankNode, parent: blankNode, subValue }\" />\n }\n }\n </tbody>\n </table>\n</he-data-table>\n<div class=\"is-size-7\">\n <div class=\"is-flex is-py-2 is-px-3 is-gap-16 | status-legend\">\n <div\n class=\"is-flex is-justify-content-center is-align-items-center is-align-content-center is-flex-wrap-wrap is-gap-8\">\n @for (status of LogStatus | keyvalue; track status.value) {\n @if (logIcon[status.value]) {\n <span class=\"is-flex is-align-items-center is-gap-8\">\n <he-svg-icon [name]=\"logIcon[status.value]\" size=\"20\" class=\"has-text-{{ logColor[status.value] }}\" />\n <span class=\"is-size-7\">{{ status.value | capitalize }}</span>\n </span>\n }\n }\n </div>\n\n @if (filteredType()) {\n <div class=\"field is-relative\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded is-secondary\"\n [(ngModel)]=\"onlyRequired\"\n [disabled]=\"!!term()\"\n id=\"onlyRequired\" />\n <label class=\"is-size-7\" for=\"onlyRequired\">\n <span>Only show {{ filteredType() }} included in the default HESTIA system boundary</span>\n </label>\n </div>\n }\n </div>\n </div>\n}\n\n<ng-template #noResultsDefaultMessage>\n <span>No original data was provided and no gap filling occurred</span>\n @if (term() && !isInSystemBoundary(term()['@id'])) {\n <span class=\"is-pl-1\">as</span>\n <i class=\"is-px-1\">{{ term().name }}</i>\n <span>is not in the HESTIA system boundary</span>\n }\n <span>.</span>\n</ng-template>\n\n<ng-template #valueContent let-value=\"value\">\n @if (isNumber(value)) {\n {{ value | precision: 3 | default: '-' }}\n } @else {\n {{ value | default: '-' }}\n }\n</ng-template>\n\n<ng-template #collapseButton let-blankNode>\n @if (blankNode.canOpen) {\n <a class=\"open-node\" (click)=\"toggleBlankNode(blankNode)\">\n <he-svg-icon [name]=\"blankNode.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n</ng-template>\n\n<ng-template #subValueRow let-blankNode=\"blankNode\" let-parent=\"parent\" let-subValue=\"subValue\" let-rowClass=\"rowClass\">\n @let term = subValue.term || termById(subValue.id);\n @if (parent.isOpen) {\n <tr [class.is-sub-row]=\"parent.canOpen\" [ngClass]=\"rowClass\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 h-100\">\n <ng-container *ngTemplateOutlet=\"collapseButton; context: { $implicit: subValue }\" />\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <div class=\"is-flex is-align-items-flex-start is-flex-wrap-wrap is-gap-4\" [class.is-pl-3]=\"!subValue.canOpen\">\n <span>\n <span>{{ subValue.key | keyToLabel }}</span>\n @if (subValue.id) {\n <span class=\"is-inline-block\">:</span>\n }\n </span>\n @if (subValue.id) {\n @switch (subValue.key) {\n @case ('backgroundData') {\n <span class=\"is-inline-block\">{{ term?.name }}</span>\n }\n @case ('animal') {\n <span class=\"is-inline-block\">{{ subValue.id }}</span>\n }\n @default {\n <he-node-link\n class=\"is-inline-block\"\n linkClass=\"is-inline-block\"\n [node]=\"term\"\n [attr.title]=\"term?.name\">\n <span class=\"break-word\" [innerHtml]=\"term?.name | compound\"></span>\n </he-node-link>\n }\n }\n }\n </div>\n </div>\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if (subValue.showUnits && $any(blankNode).term) {\n <span class=\"is-nowrap\" [innerHtml]=\"$any(blankNode).term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n @if (!isEmpty(subValue.originalValue)) {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.originalValue }\" />\n </span>\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (subValue.isRecalculated) {\n @if (subValue.multiGroups) {\n <span\n class=\"trigger-popover\"\n ngbPopover=\"The total value across all inputs\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\">\n <span pointer>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n </span>\n } @else {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n }\n } @else if (!isEmpty(subValue.originalValue)) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">-</td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: subValue }\" />\n </tr>\n }\n @if (subValue.subValues) {\n @for (sub of subValue.subValues; track trackBySubValue(sub)) {\n <ng-container\n *ngTemplateOutlet=\"\n subValueRow;\n context: { blankNode, parent: subValue, subValue: sub, rowClass: 'is-sub-sub-row' }\n \" />\n }\n }\n</ng-template>\n\n<ng-template #blankNodeOriginalValueDetails let-blankNode=\"blankNode\">\n <span class=\"is-pr-1\">The original value was reported using:</span>\n <he-node-link\n class=\"is-inline-block\"\n linkClass=\"is-dark\"\n [node]=\"$any(blankNode).original[0].methodModel\"\n [showExternalLink]=\"true\">\n <span>{{ $any(blankNode).original[0].methodModel.name }}</span>\n </he-node-link>\n</ng-template>\n\n<ng-template #blankNodeModels let-data=\"data\">\n @let extraColumns = methodModelsCount() - 1;\n\n <ng-template #notInSystemBoundary>\n <td class=\"has-border-right\">\n <span>Not in HESTIA system boundary</span>\n </td>\n @for (v of data | repeat: extraColumns; track repeatIndex; let repeatIndex = $index) {\n <td class=\"has-border-right\"></td>\n }\n </ng-template>\n\n @if (data.canOpen && !data.isOpen && !data.configModels?.length) {\n <td class=\"has-border-right\">\n <span>Expand to see logs (</span>\n @let key = subValuesKey(data, 'sub-values');\n @if (hasCompleteSuccess(data)) {\n <span>all succeeded</span>\n <he-svg-icon class=\"is-ml-1\" name=\"checkmark\" class=\"has-text-success\" />\n } @else {\n <span>some failed</span>\n <he-svg-icon class=\"is-ml-1\" name=\"xmark\" class=\"has-text-danger\" />\n }\n <span>)</span>\n </td>\n @for (v of data | repeat: extraColumns; track repeatIndex; let repeatIndex = $index) {\n <td class=\"has-border-right\"></td>\n }\n } @else {\n @for (configModel of methodModelsCount() | times; track configModelIndex; let configModelIndex = $index) {\n <td class=\"has-border-right blank-node-index-{{ configModelIndex }}\">\n @if (getModelsAt(data, configModelIndex); as models) {\n @if ($any(models) | isArray) {\n <div>\n @for (model of $any(models); track model.methodId) {\n <p>\n <ng-container *ngTemplateOutlet=\"blankNodeModel; context: { model, data }\" />\n </p>\n }\n </div>\n } @else {\n <ng-container *ngTemplateOutlet=\"blankNodeModel; context: { model: models, data }\" />\n }\n } @else {\n -\n }\n </td>\n }\n }\n</ng-template>\n\n<ng-template #blankNodeModel let-model=\"model\" let-data=\"data\">\n <div class=\"is-flex is-align-self-stretch is-justify-content-center is-align-items-center is-gap-8\">\n <div class=\"is-flex is-gap-4 is-flex-grow-1 is-align-items-center\">\n <span\n class=\"pl-1 has-text-{{ logColor[model.status] }}\"\n [class.trigger-popover]=\"hasLogs()\"\n [ngbPopover]=\"logStatusDetails\"\n [disablePopover]=\"!hasLogs()\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p1=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"$event.stopPropagation(); p1.isOpen() ? p1.close() : p1.open({ model, data })\">\n <he-svg-icon [name]=\"logIcon[model.status]\" />\n </span>\n\n <span class=\"is-flex is-flex-grow-1 is-gap-4\">\n <span class=\"is-nowrap is-capitalized\">{{ methodName(model) }}</span>\n\n @if (model.logs?.methodTier || model.model?.methodTier) {\n <span class=\"is-nowrap\">[{{ model.logs?.methodTier || model.model?.methodTier }}]</span>\n }\n </span>\n </div>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (model.showLogs) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"logDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : model.showLogs ? p.open({ model }) : null\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n }\n\n @if (model.model) {\n @if (model.showLogs) {\n <div class=\"vertical-divider\"></div>\n }\n <ng-container *ngTemplateOutlet=\"docsLink; context: { $implicit: model.model }\" />\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #logDetails let-model=\"model\">\n @if (hasContributions()) {\n @if (showContributionsLogs()) {\n <div class=\"has-text-right is-mb-1\">\n <button class=\"button is-white is-small is-outlined\" (click)=\"showContributionsLogs.set(false)\">\n Hide contributions details\n </button>\n </div>\n <he-node-logs-models-contributions [node]=\"node()\" [nodeKey]=\"nodeKey()\" [model]=\"model\" />\n } @else {\n <div class=\"has-text-right is-mb-1\">\n <button class=\"button is-white is-small is-outlined\" (click)=\"showContributionsLogs.set(true)\">\n Show contributions details\n </button>\n </div>\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n }\n } @else {\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n }\n</ng-template>\n\n<ng-template #logStatusDetails let-model=\"model\" let-data=\"data\">\n <he-node-logs-models-logs-status [nodeType]=\"nodeType()\" [model]=\"model\" [data]=\"\" />\n</ng-template>\n\n<ng-template #docsLink let-model>\n @if (guideEnabled && model.guidePath) {\n <he-guide-overlay [pageId]=\"model.guidePath\" [width]=\"500\" />\n } @else {\n <a [href]=\"model.docPath || model.path\" target=\"_blank\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .status-legend{border:1px solid #dbe3ea;background:#f5f7f9}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row td:first-child{padding-left:12px}::ng-deep .table .is-sub-row .sub-sub-row-icon{display:none}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table .is-sub-sub-row .sub-sub-row-icon{display:inline-block}::ng-deep .table .is-sub-sub-row .sub-sub-row-icon+div{padding-left:0!important}::ng-deep .table .popover-body .table-container{max-height:260px;overflow-y:auto}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeValueDeltaComponent, selector: "he-blank-node-value-delta", inputs: ["value", "originalValue", "displayType", "useCustomFunctions"] }, { kind: "directive", type: NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: NodeLogsModelsLogsComponent, selector: "he-node-logs-models-logs", inputs: ["logs"] }, { kind: "component", type: NodeLogsModelsLogsStatusComponent, selector: "he-node-logs-models-logs-status", inputs: ["nodeType", "model", "data"] }, { kind: "component", type: NodeLogsModelsContributionsComponent, selector: "he-node-logs-models-contributions", inputs: ["node", "nodeKey", "model"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: GuideOverlayComponent, selector: "he-guide-overlay", inputs: ["pageId", "width", "height", "positions"], outputs: ["widthChange", "heightChange"] }, { kind: "component", type: NodeJLogModelsComponent, selector: "he-node-jlog-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "filterTermTypes", "filterTermTypesLabel", "cycle", "jlog", "jlogParentKey", "jlogParentIndex"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: KeyToLabelPipe, name: "keyToLabel" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: TimesPipe, name: "times" }, { kind: "pipe", type: IsArrayPipe, name: "isArray" }, { kind: "pipe", type: RepeatPipe, name: "repeat" }, { kind: "pipe", type: CapitalizePipe, name: "capitalize" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9467
10386
  }
9468
10387
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeLogsModelsComponent, decorators: [{
9469
10388
  type: Component$1,
@@ -9489,9 +10408,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
9489
10408
  NodeLogsModelsLogsStatusComponent,
9490
10409
  NodeLogsModelsContributionsComponent,
9491
10410
  HESvgIconComponent,
9492
- GuideOverlayComponent
9493
- ], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n\n @if (!isExternal && !loading() && logsUrl() && hasLogs()) {\n <a class=\"is-size-7\" [href]=\"logsUrl()\" target=\"_blank\">\n <span>Open Full Logs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</div>\n\n@if (!isExternal && !loading() && !hasLogs()) {\n <p class=\"is-my-2\">\n <he-svg-icon class=\"has-text-warning\" name=\"exclamation-triangle\" />\n <span class=\"is-size-7 has-text-warning is-pl-1\">No logs found. Recalculation logs will be incomplete.</span>\n </p>\n}\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\">\n @if (enableFilterByTerm()) {\n <div class=\"field is-pb-1\">\n <div class=\"control is-expanded has-icons-right\">\n <input\n class=\"input search-input is-small\"\n [ngModel]=\"term()\"\n name=\"term\"\n placeholder=\"Select entry by name\"\n [ngbTypeahead]=\"suggestTerm\"\n [resultFormatter]=\"termFormatter\"\n [inputFormatter]=\"termFormatter\"\n [focusFirst]=\"true\"\n (focus)=\"typeaheadFocus($event)\"\n (selectItem)=\"term.set($event.item)\"\n container=\"body\"\n popupClass=\"is-small\" />\n <a class=\"icon is-small is-right\" [class.is-hidden]=\"!term()\" (click)=\"term.set(undefined)\">\n <he-svg-icon name=\"xmark\" />\n </a>\n </div>\n </div>\n }\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>\n <span class=\"is-pr-1\">Units</span>\n @if (functionalUnit()) {\n <span>(per&nbsp;</span>\n <span>{{ functionalUnit() }}</span>\n <span>)</span>\n }\n </span>\n </div>\n </th>\n }\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Original</span>\n </div>\n </th>\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Recalculated</span>\n </div>\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Difference</span>\n </div>\n </th>\n }\n @for (c of methodModelsCount() | times; track i; let i = $index) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Model {{ i + 1 }}</span>\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (loading()) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <div class=\"has-text-center py-3\">\n <he-svg-icon name=\"loading\" animation=\"spin\" size=\"40\" />\n </div>\n </td>\n </tr>\n } @else if (blankNodes().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">\n @if (noDataMessage()) {\n <span>{{ noDataMessage() }}</span>\n } @else {\n <ng-container *ngTemplateOutlet=\"noResultsDefaultMessage\" />\n }\n </p>\n </td>\n </tr>\n }\n @for (blankNode of blankNodes(); track trackByBlankNode($index, blankNode)) {\n @let configModels = $any(blankNode).configModels;\n <tr [class.has-sub-rows]=\"blankNode.canOpen\" [class.is-open]=\"blankNode.isOpen\">\n <td\n class=\"width-auto has-border-right is-nowrap\"\n [attr.title]=\"$any(blankNode).term?.name || $any(blankNode).key\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n <ng-container *ngTemplateOutlet=\"collapseButton; context: { $implicit: blankNode }\" />\n @if ($any(blankNode).term) {\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"$any(blankNode).term\">\n <span\n class=\"break-word\"\n [innerHtml]=\"$any(blankNode).term.name | compound: $any(blankNode).term.termType\"></span>\n </he-node-link>\n } @else if ($any(blankNode).key) {\n @if (nodeKey() === 'completeness') {\n <a [href]=\"schemaBaseUrl + '/Completeness#' + $any(blankNode).key\" target=\"_blank\">\n <span>{{ $any(blankNode).key | keyToLabel }}</span>\n </a>\n } @else {\n <a [href]=\"schemaBaseUrl + '/' + nodeType() + '#' + $any(blankNode).key\" target=\"_blank\">\n <span>{{ $any(blankNode).key | keyToLabel }}</span>\n </a>\n }\n }\n </div>\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if ($any(blankNode).term) {\n <span class=\"is-nowrap\" [innerHtml]=\"$any(blankNode).term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n <ng-template #originalValueContent>\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: $any(blankNode).originalValue }\" />\n </span>\n </ng-template>\n\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n <ng-container\n *ngTemplateOutlet=\"\n valueContent;\n context: { value: $any(blankNode).originalValueByMethodId[model.methodId] }\n \" />\n </div>\n }\n } @else {\n <span\n [class.trigger-popover]=\"!!$any(blankNode).original?.[0]?.methodModel\"\n [ngbPopover]=\"blankNodeOriginalValueDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n placement=\"bottom left right auto\"\n container=\"body\"\n [disablePopover]=\"!$any(blankNode).original?.[0]?.methodModel\"\n [popoverContext]=\"{ blankNode }\">\n <span pointer>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: $any(blankNode).originalValue }\" />\n </span>\n </span>\n }\n </td>\n <td class=\"has-border-right\">\n @if (blankNode.isRecalculated) {\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n <ng-container\n *ngTemplateOutlet=\"\n valueContent;\n context: { value: $any(blankNode).recalculatedValueByMethodId[model.methodId] }\n \" />\n </div>\n }\n } @else {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: blankNode.recalculatedValue }\" />\n </span>\n }\n } @else if (configModels?.length) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right is-nowrap\">\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n @if (\n $any(blankNode).originalValueByMethodId[model.methodId] !== null &&\n $any(blankNode).recalculatedValueByMethodId[model.methodId] !== null\n ) {\n <he-blank-node-value-delta\n [value]=\"$any(blankNode).recalculatedValueByMethodId[model.methodId]\"\n [originalValue]=\"$any(blankNode).originalValueByMethodId[model.methodId]\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n </div>\n }\n } @else {\n @if ($any(blankNode).original.length && blankNode.isRecalculated) {\n <he-blank-node-value-delta\n [value]=\"blankNode.recalculatedValue\"\n [originalValue]=\"$any(blankNode).originalValue\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n }\n </td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: blankNode }\" />\n </tr>\n @for (subValue of $any(blankNode).keys; track trackBySubValue(subValue)) {\n @if (blankNode.isOpen) {\n <tr [class.has-sub-rows]=\"$any(blankNode).subValues?.length\" [class.is-sub-row]=\"blankNode.canOpen\">\n <td class=\"width-auto has-border-right is-nowrap\">\n <span class=\"is-inline-block is-align-top pl-3 pr-1 field-node\">Field:</span>\n @if (blankNode.type) {\n <a\n class=\"is-inline-block is-pre-wrap\"\n [href]=\"schemaBaseUrl + '/' + blankNode.type + '#' + subValue.key\"\n target=\"_blank\"\n [title]=\"subValue.key\">\n <span>{{ subValue.key }}</span>\n </a>\n }\n @if (!blankNode.type) {\n <span class=\"is-inline-block is-align-top\">{{ subValue.key }}</span>\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\"></td>\n }\n <td class=\"has-border-right\">\n @if (subValue.originalValue !== null) {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.originalValue }\" />\n </span>\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (subValue.isRecalculated || subValue.key === 'impactAssessment') {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n } @else {\n not recalculated\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">-</td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: subValue }\" />\n </tr>\n }\n }\n @for (subValue of $any(blankNode).subValues; track trackBySubValue(subValue)) {\n <ng-container *ngTemplateOutlet=\"subValueRow; context: { blankNode, parent: blankNode, subValue }\" />\n }\n }\n </tbody>\n </table>\n</he-data-table>\n<div class=\"is-size-7\">\n <div class=\"is-flex is-py-2 is-px-3 is-gap-16 | status-legend\">\n <div\n class=\"is-flex is-justify-content-center is-align-items-center is-align-content-center is-flex-wrap-wrap is-gap-8\">\n @for (status of LogStatus | keyvalue; track status.value) {\n @if (logIcon[status.value]) {\n <span class=\"is-flex is-align-items-center is-gap-8\">\n <he-svg-icon [name]=\"logIcon[status.value]\" size=\"20\" class=\"has-text-{{ logColor[status.value] }}\" />\n <span class=\"is-size-7\">{{ status.value | capitalize }}</span>\n </span>\n }\n }\n </div>\n\n @if (filteredType()) {\n <div class=\"field is-relative\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded is-secondary\"\n [(ngModel)]=\"onlyRequired\"\n [disabled]=\"!!term()\"\n id=\"onlyRequired\" />\n <label class=\"is-size-7\" for=\"onlyRequired\">\n <span>Only show {{ filteredType() }} included in the default HESTIA system boundary</span>\n </label>\n </div>\n }\n </div>\n</div>\n\n<ng-template #noResultsDefaultMessage>\n <span>No original data was provided and no gap filling occurred</span>\n @if (term() && !isInSystemBoundary(term()['@id'])) {\n <span class=\"is-pl-1\">as</span>\n <i class=\"is-px-1\">{{ term().name }}</i>\n <span>is not in the HESTIA system boundary</span>\n }\n <span>.</span>\n</ng-template>\n\n<ng-template #valueContent let-value=\"value\">\n @if (isNumber(value)) {\n {{ value | precision: 3 | default: '-' }}\n } @else {\n {{ value | default: '-' }}\n }\n</ng-template>\n\n<ng-template #collapseButton let-blankNode>\n @if (blankNode.canOpen) {\n <a class=\"open-node\" (click)=\"toggleBlankNode(blankNode)\">\n <he-svg-icon [name]=\"blankNode.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n</ng-template>\n\n<ng-template #subValueRow let-blankNode=\"blankNode\" let-parent=\"parent\" let-subValue=\"subValue\" let-rowClass=\"rowClass\">\n @let term = subValue.term || termById(subValue.id);\n @if (parent.isOpen) {\n <tr [class.is-sub-row]=\"parent.canOpen\" [ngClass]=\"rowClass\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 h-100\">\n <ng-container *ngTemplateOutlet=\"collapseButton; context: { $implicit: subValue }\" />\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <div class=\"is-flex is-align-items-flex-start is-flex-wrap-wrap is-gap-4\" [class.is-pl-3]=\"!subValue.canOpen\">\n <span>\n <span>{{ subValue.key | keyToLabel }}</span>\n @if (subValue.id) {\n <span class=\"is-inline-block\">:</span>\n }\n </span>\n @if (subValue.id) {\n @switch (subValue.key) {\n @case ('backgroundData') {\n <span class=\"is-inline-block\">{{ term?.name }}</span>\n }\n @case ('animal') {\n <span class=\"is-inline-block\">{{ subValue.id }}</span>\n }\n @default {\n <he-node-link\n class=\"is-inline-block\"\n linkClass=\"is-inline-block\"\n [node]=\"term\"\n [attr.title]=\"term?.name\">\n <span class=\"break-word\" [innerHtml]=\"term?.name | compound\"></span>\n </he-node-link>\n }\n }\n }\n </div>\n </div>\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if (subValue.showUnits && $any(blankNode).term) {\n <span class=\"is-nowrap\" [innerHtml]=\"$any(blankNode).term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n @if (!isEmpty(subValue.originalValue)) {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.originalValue }\" />\n </span>\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (subValue.isRecalculated) {\n @if (subValue.multiGroups) {\n <span\n class=\"trigger-popover\"\n ngbPopover=\"The total value across all inputs\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\">\n <span pointer>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n </span>\n } @else {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n }\n } @else if (!isEmpty(subValue.originalValue)) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">-</td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: subValue }\" />\n </tr>\n }\n @if (subValue.subValues) {\n @for (sub of subValue.subValues; track trackBySubValue(sub)) {\n <ng-container\n *ngTemplateOutlet=\"\n subValueRow;\n context: { blankNode, parent: subValue, subValue: sub, rowClass: 'is-sub-sub-row' }\n \" />\n }\n }\n</ng-template>\n\n<ng-template #blankNodeOriginalValueDetails let-blankNode=\"blankNode\">\n <span class=\"is-pr-1\">The original value was reported using:</span>\n <he-node-link\n class=\"is-inline-block\"\n linkClass=\"is-dark\"\n [node]=\"$any(blankNode).original[0].methodModel\"\n [showExternalLink]=\"true\">\n <span>{{ $any(blankNode).original[0].methodModel.name }}</span>\n </he-node-link>\n</ng-template>\n\n<ng-template #blankNodeModels let-data=\"data\">\n @let extraColumns = methodModelsCount() - 1;\n\n <ng-template #notInSystemBoundary>\n <td class=\"has-border-right\">\n <span>Not in HESTIA system boundary</span>\n </td>\n @for (v of data | repeat: extraColumns; track repeatIndex; let repeatIndex = $index) {\n <td class=\"has-border-right\"></td>\n }\n </ng-template>\n\n @if (data.canOpen && !data.isOpen && !data.configModels?.length) {\n <td class=\"has-border-right\">\n <span>Expand to see logs (</span>\n @let key = subValuesKey(data, 'sub-values');\n @if (hasCompleteSuccess(data)) {\n <span>all succeeded</span>\n <he-svg-icon class=\"is-ml-1\" name=\"checkmark\" class=\"has-text-success\" />\n } @else {\n <span>some failed</span>\n <he-svg-icon class=\"is-ml-1\" name=\"xmark\" class=\"has-text-danger\" />\n }\n <span>)</span>\n </td>\n @for (v of data | repeat: extraColumns; track repeatIndex; let repeatIndex = $index) {\n <td class=\"has-border-right\"></td>\n }\n } @else {\n @for (configModel of methodModelsCount() | times; track configModelIndex; let configModelIndex = $index) {\n <td class=\"has-border-right blank-node-index-{{ configModelIndex }}\">\n @if (getModelsAt(data, configModelIndex); as models) {\n @if ($any(models) | isArray) {\n <div>\n @for (model of $any(models); track model.methodId) {\n <p>\n <ng-container *ngTemplateOutlet=\"blankNodeModel; context: { model, data }\" />\n </p>\n }\n </div>\n } @else {\n <ng-container *ngTemplateOutlet=\"blankNodeModel; context: { model: models, data }\" />\n }\n } @else {\n -\n }\n </td>\n }\n }\n</ng-template>\n\n<ng-template #blankNodeModel let-model=\"model\" let-data=\"data\">\n <div class=\"is-flex is-align-self-stretch is-justify-content-center is-align-items-center is-gap-8\">\n <div class=\"is-flex is-gap-4 is-flex-grow-1 is-align-items-center\">\n <span\n class=\"pl-1 has-text-{{ logColor[model.status] }}\"\n [class.trigger-popover]=\"hasLogs()\"\n [ngbPopover]=\"logStatusDetails\"\n [disablePopover]=\"!hasLogs()\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p1=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"$event.stopPropagation(); p1.isOpen() ? p1.close() : p1.open({ model, data })\">\n <he-svg-icon [name]=\"logIcon[model.status]\" />\n </span>\n\n <span class=\"is-flex is-flex-grow-1 is-gap-4\">\n <span class=\"is-nowrap is-capitalized\">{{ methodName(model) }}</span>\n\n @if (model.logs?.methodTier || model.model?.methodTier) {\n <span class=\"is-nowrap\">[{{ model.logs?.methodTier || model.model?.methodTier }}]</span>\n }\n </span>\n </div>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (model.showLogs) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"logDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : model.showLogs ? p.open({ model }) : null\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n }\n\n @if (model.model) {\n @if (model.showLogs) {\n <div class=\"vertical-divider\"></div>\n }\n <ng-container *ngTemplateOutlet=\"docsLink; context: { $implicit: model.model }\" />\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #logDetails let-model=\"model\">\n @if (hasContributions()) {\n @if (showContributionsLogs()) {\n <div class=\"has-text-right is-mb-1\">\n <button class=\"button is-white is-small is-outlined\" (click)=\"showContributionsLogs.set(false)\">\n Hide contributions details\n </button>\n </div>\n <he-node-logs-models-contributions [node]=\"node()\" [nodeKey]=\"nodeKey()\" [model]=\"model\" />\n } @else {\n <div class=\"has-text-right is-mb-1\">\n <button class=\"button is-white is-small is-outlined\" (click)=\"showContributionsLogs.set(true)\">\n Show contributions details\n </button>\n </div>\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n }\n } @else {\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n }\n</ng-template>\n\n<ng-template #logStatusDetails let-model=\"model\" let-data=\"data\">\n <he-node-logs-models-logs-status [nodeType]=\"nodeType()\" [model]=\"model\" [data]=\"\" />\n</ng-template>\n\n<ng-template #docsLink let-model>\n @if (guideEnabled && model.guidePath) {\n <he-guide-overlay [pageId]=\"model.guidePath\" [width]=\"500\" />\n } @else {\n <a [href]=\"model.docPath || model.path\" target=\"_blank\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .status-legend{border:1px solid #dbe3ea;background:#f5f7f9}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row td:first-child{padding-left:12px}::ng-deep .table .is-sub-row .sub-sub-row-icon{display:none}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table .is-sub-sub-row .sub-sub-row-icon{display:inline-block}::ng-deep .table .is-sub-sub-row .sub-sub-row-icon+div{padding-left:0!important}::ng-deep .table .popover-body .table-container{max-height:260px;overflow-y:auto}\n"] }]
9494
- }], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }], nodeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKey", required: false }] }], originalValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "originalValues", required: false }] }], recalculatedValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "recalculatedValues", required: false }] }], terms: [{ type: i0.Input, args: [{ isSignal: true, alias: "terms", required: false }] }], filterTermTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterTermTypes", required: false }] }], filterTermTypesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterTermTypesLabel", required: false }] }], logsKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "logsKey", required: false }] }], noDataMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "noDataMessage", required: false }] }], cycle: [{ type: i0.Input, args: [{ isSignal: true, alias: "cycle", required: false }] }] } });
10411
+ GuideOverlayComponent,
10412
+ NodeJLogModelsComponent
10413
+ ], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n\n @if (!isExternal && !loading() && logsUrl() && hasLogs()) {\n <a class=\"is-size-7\" [href]=\"logsUrl()\" target=\"_blank\">\n <span>Open Full Logs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</div>\n\n@if (!isExternal && !loading() && !loadingJLog() && !hasLogs()) {\n <p class=\"is-my-2\">\n <he-svg-icon class=\"has-text-warning\" name=\"exclamation-triangle\" />\n <span class=\"is-size-7 has-text-warning is-pl-1\">No logs found. Recalculation logs will be incomplete.</span>\n </p>\n}\n\n@if (loadingJLog()) {\n <p class=\"has-text-centered py-3\">\n <he-svg-icon name=\"loading\" animation=\"spin\" size=\"40\" />\n </p>\n} @else if (useJLog()) {\n <he-node-jlog-models\n [node]=\"node()\"\n [nodeKey]=\"$any(nodeKey())\"\n [originalValues]=\"$any(originalValues())\"\n [recalculatedValues]=\"$any(recalculatedValues())\"\n [filterTermTypes]=\"filterTermTypes()\"\n [filterTermTypesLabel]=\"filterTermTypesLabel()\"\n [cycle]=\"cycle()\"\n [jlog]=\"jlog()\"\n [jlogParentKey]=\"jlogParentKey()\"\n [jlogParentIndex]=\"jlogParentIndex()\" />\n} @else {\n <he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\">\n @if (enableFilterByTerm()) {\n <div class=\"field is-pb-1\">\n <div class=\"control is-expanded has-icons-right\">\n <input\n class=\"input search-input is-small\"\n [ngModel]=\"term()\"\n name=\"term\"\n placeholder=\"Select entry by name\"\n [ngbTypeahead]=\"suggestTerm\"\n [resultFormatter]=\"termFormatter\"\n [inputFormatter]=\"termFormatter\"\n [focusFirst]=\"true\"\n (focus)=\"typeaheadFocus($event)\"\n (selectItem)=\"term.set($event.item)\"\n container=\"body\"\n popupClass=\"is-small\" />\n <a class=\"icon is-small is-right\" [class.is-hidden]=\"!term()\" (click)=\"term.set(undefined)\">\n <he-svg-icon name=\"xmark\" />\n </a>\n </div>\n </div>\n }\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>\n <span class=\"is-pr-1\">Units</span>\n @if (functionalUnit()) {\n <span>(per&nbsp;</span>\n <span>{{ functionalUnit() }}</span>\n <span>)</span>\n }\n </span>\n </div>\n </th>\n }\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Original</span>\n </div>\n </th>\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Recalculated</span>\n </div>\n </th>\n @if (isBlankNodes()) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Difference</span>\n </div>\n </th>\n }\n @for (c of methodModelsCount() | times; track i; let i = $index) {\n <th class=\"has-border-right\">\n <div class=\"is-flex is-flex-direction-column is-justify-content-center h-100\">\n <span>Model {{ i + 1 }}</span>\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (loading()) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <div class=\"has-text-center py-3\">\n <he-svg-icon name=\"loading\" animation=\"spin\" size=\"40\" />\n </div>\n </td>\n </tr>\n } @else if (blankNodes().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">\n @if (noDataMessage()) {\n <span>{{ noDataMessage() }}</span>\n } @else {\n <ng-container *ngTemplateOutlet=\"noResultsDefaultMessage\" />\n }\n </p>\n </td>\n </tr>\n }\n @for (blankNode of blankNodes(); track trackByBlankNode($index, blankNode)) {\n @let configModels = $any(blankNode).configModels;\n <tr [class.has-sub-rows]=\"blankNode.canOpen\" [class.is-open]=\"blankNode.isOpen\">\n <td\n class=\"width-auto has-border-right is-nowrap\"\n [attr.title]=\"$any(blankNode).term?.name || $any(blankNode).key\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n <ng-container *ngTemplateOutlet=\"collapseButton; context: { $implicit: blankNode }\" />\n @if ($any(blankNode).term) {\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"$any(blankNode).term\">\n <span\n class=\"break-word\"\n [innerHtml]=\"$any(blankNode).term.name | compound: $any(blankNode).term.termType\"></span>\n </he-node-link>\n } @else if ($any(blankNode).key) {\n @if (nodeKey() === 'completeness') {\n <a [href]=\"schemaBaseUrl + '/Completeness#' + $any(blankNode).key\" target=\"_blank\">\n <span>{{ $any(blankNode).key | keyToLabel }}</span>\n </a>\n } @else {\n <a [href]=\"schemaBaseUrl + '/' + nodeType() + '#' + $any(blankNode).key\" target=\"_blank\">\n <span>{{ $any(blankNode).key | keyToLabel }}</span>\n </a>\n }\n }\n </div>\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if ($any(blankNode).term) {\n <span class=\"is-nowrap\" [innerHtml]=\"$any(blankNode).term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n <ng-template #originalValueContent>\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: $any(blankNode).originalValue }\" />\n </span>\n </ng-template>\n\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n <ng-container\n *ngTemplateOutlet=\"\n valueContent;\n context: { value: $any(blankNode).originalValueByMethodId[model.methodId] }\n \" />\n </div>\n }\n } @else {\n <span\n [class.trigger-popover]=\"!!$any(blankNode).original?.[0]?.methodModel\"\n [ngbPopover]=\"blankNodeOriginalValueDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n placement=\"bottom left right auto\"\n container=\"body\"\n [disablePopover]=\"!$any(blankNode).original?.[0]?.methodModel\"\n [popoverContext]=\"{ blankNode }\">\n <span pointer>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: $any(blankNode).originalValue }\" />\n </span>\n </span>\n }\n </td>\n <td class=\"has-border-right\">\n @if (blankNode.isRecalculated) {\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n <ng-container\n *ngTemplateOutlet=\"\n valueContent;\n context: { value: $any(blankNode).recalculatedValueByMethodId[model.methodId] }\n \" />\n </div>\n }\n } @else {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: blankNode.recalculatedValue }\" />\n </span>\n }\n } @else if (configModels?.length) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right is-nowrap\">\n @if (blankNode.allParallel) {\n @for (model of configModels[0]; track model.methodId) {\n <div>\n @if (\n $any(blankNode).originalValueByMethodId[model.methodId] !== null &&\n $any(blankNode).recalculatedValueByMethodId[model.methodId] !== null\n ) {\n <he-blank-node-value-delta\n [value]=\"$any(blankNode).recalculatedValueByMethodId[model.methodId]\"\n [originalValue]=\"$any(blankNode).originalValueByMethodId[model.methodId]\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n </div>\n }\n } @else {\n @if ($any(blankNode).original.length && blankNode.isRecalculated) {\n <he-blank-node-value-delta\n [value]=\"blankNode.recalculatedValue\"\n [originalValue]=\"$any(blankNode).originalValue\"\n [useCustomFunctions]=\"false\" />\n } @else {\n -\n }\n }\n </td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: blankNode }\" />\n </tr>\n @for (subValue of $any(blankNode).keys; track trackBySubValue(subValue)) {\n @if (blankNode.isOpen) {\n <tr [class.has-sub-rows]=\"$any(blankNode).subValues?.length\" [class.is-sub-row]=\"blankNode.canOpen\">\n <td class=\"width-auto has-border-right is-nowrap\">\n <span class=\"is-inline-block is-align-top pl-3 pr-1 field-node\">Field:</span>\n @if (blankNode.type) {\n <a\n class=\"is-inline-block is-pre-wrap\"\n [href]=\"schemaBaseUrl + '/' + blankNode.type + '#' + subValue.key\"\n target=\"_blank\"\n [title]=\"subValue.key\">\n <span>{{ subValue.key }}</span>\n </a>\n }\n @if (!blankNode.type) {\n <span class=\"is-inline-block is-align-top\">{{ subValue.key }}</span>\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\"></td>\n }\n <td class=\"has-border-right\">\n @if (subValue.originalValue !== null) {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.originalValue }\" />\n </span>\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (subValue.isRecalculated || subValue.key === 'impactAssessment') {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n } @else {\n not recalculated\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">-</td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: subValue }\" />\n </tr>\n }\n }\n @for (subValue of $any(blankNode).subValues; track trackBySubValue(subValue)) {\n <ng-container *ngTemplateOutlet=\"subValueRow; context: { blankNode, parent: blankNode, subValue }\" />\n }\n }\n </tbody>\n </table>\n</he-data-table>\n<div class=\"is-size-7\">\n <div class=\"is-flex is-py-2 is-px-3 is-gap-16 | status-legend\">\n <div\n class=\"is-flex is-justify-content-center is-align-items-center is-align-content-center is-flex-wrap-wrap is-gap-8\">\n @for (status of LogStatus | keyvalue; track status.value) {\n @if (logIcon[status.value]) {\n <span class=\"is-flex is-align-items-center is-gap-8\">\n <he-svg-icon [name]=\"logIcon[status.value]\" size=\"20\" class=\"has-text-{{ logColor[status.value] }}\" />\n <span class=\"is-size-7\">{{ status.value | capitalize }}</span>\n </span>\n }\n }\n </div>\n\n @if (filteredType()) {\n <div class=\"field is-relative\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded is-secondary\"\n [(ngModel)]=\"onlyRequired\"\n [disabled]=\"!!term()\"\n id=\"onlyRequired\" />\n <label class=\"is-size-7\" for=\"onlyRequired\">\n <span>Only show {{ filteredType() }} included in the default HESTIA system boundary</span>\n </label>\n </div>\n }\n </div>\n </div>\n}\n\n<ng-template #noResultsDefaultMessage>\n <span>No original data was provided and no gap filling occurred</span>\n @if (term() && !isInSystemBoundary(term()['@id'])) {\n <span class=\"is-pl-1\">as</span>\n <i class=\"is-px-1\">{{ term().name }}</i>\n <span>is not in the HESTIA system boundary</span>\n }\n <span>.</span>\n</ng-template>\n\n<ng-template #valueContent let-value=\"value\">\n @if (isNumber(value)) {\n {{ value | precision: 3 | default: '-' }}\n } @else {\n {{ value | default: '-' }}\n }\n</ng-template>\n\n<ng-template #collapseButton let-blankNode>\n @if (blankNode.canOpen) {\n <a class=\"open-node\" (click)=\"toggleBlankNode(blankNode)\">\n <he-svg-icon [name]=\"blankNode.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n</ng-template>\n\n<ng-template #subValueRow let-blankNode=\"blankNode\" let-parent=\"parent\" let-subValue=\"subValue\" let-rowClass=\"rowClass\">\n @let term = subValue.term || termById(subValue.id);\n @if (parent.isOpen) {\n <tr [class.is-sub-row]=\"parent.canOpen\" [ngClass]=\"rowClass\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 h-100\">\n <ng-container *ngTemplateOutlet=\"collapseButton; context: { $implicit: subValue }\" />\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <div class=\"is-flex is-align-items-flex-start is-flex-wrap-wrap is-gap-4\" [class.is-pl-3]=\"!subValue.canOpen\">\n <span>\n <span>{{ subValue.key | keyToLabel }}</span>\n @if (subValue.id) {\n <span class=\"is-inline-block\">:</span>\n }\n </span>\n @if (subValue.id) {\n @switch (subValue.key) {\n @case ('backgroundData') {\n <span class=\"is-inline-block\">{{ term?.name }}</span>\n }\n @case ('animal') {\n <span class=\"is-inline-block\">{{ subValue.id }}</span>\n }\n @default {\n <he-node-link\n class=\"is-inline-block\"\n linkClass=\"is-inline-block\"\n [node]=\"term\"\n [attr.title]=\"term?.name\">\n <span class=\"break-word\" [innerHtml]=\"term?.name | compound\"></span>\n </he-node-link>\n }\n }\n }\n </div>\n </div>\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">\n @if (subValue.showUnits && $any(blankNode).term) {\n <span class=\"is-nowrap\" [innerHtml]=\"$any(blankNode).term.units | compound\"></span>\n }\n </td>\n }\n <td class=\"has-border-right\">\n @if (!isEmpty(subValue.originalValue)) {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.originalValue }\" />\n </span>\n } @else {\n -\n }\n </td>\n <td class=\"has-border-right\">\n @if (subValue.isRecalculated) {\n @if (subValue.multiGroups) {\n <span\n class=\"trigger-popover\"\n ngbPopover=\"The total value across all inputs\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\">\n <span pointer>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n </span>\n } @else {\n <span>\n <ng-container *ngTemplateOutlet=\"valueContent; context: { value: subValue.recalculatedValue }\" />\n </span>\n }\n } @else if (!isEmpty(subValue.originalValue)) {\n not recalculated\n } @else {\n -\n }\n </td>\n @if (isBlankNodes()) {\n <td class=\"has-border-right\">-</td>\n }\n <ng-container *ngTemplateOutlet=\"blankNodeModels; context: { data: subValue }\" />\n </tr>\n }\n @if (subValue.subValues) {\n @for (sub of subValue.subValues; track trackBySubValue(sub)) {\n <ng-container\n *ngTemplateOutlet=\"\n subValueRow;\n context: { blankNode, parent: subValue, subValue: sub, rowClass: 'is-sub-sub-row' }\n \" />\n }\n }\n</ng-template>\n\n<ng-template #blankNodeOriginalValueDetails let-blankNode=\"blankNode\">\n <span class=\"is-pr-1\">The original value was reported using:</span>\n <he-node-link\n class=\"is-inline-block\"\n linkClass=\"is-dark\"\n [node]=\"$any(blankNode).original[0].methodModel\"\n [showExternalLink]=\"true\">\n <span>{{ $any(blankNode).original[0].methodModel.name }}</span>\n </he-node-link>\n</ng-template>\n\n<ng-template #blankNodeModels let-data=\"data\">\n @let extraColumns = methodModelsCount() - 1;\n\n <ng-template #notInSystemBoundary>\n <td class=\"has-border-right\">\n <span>Not in HESTIA system boundary</span>\n </td>\n @for (v of data | repeat: extraColumns; track repeatIndex; let repeatIndex = $index) {\n <td class=\"has-border-right\"></td>\n }\n </ng-template>\n\n @if (data.canOpen && !data.isOpen && !data.configModels?.length) {\n <td class=\"has-border-right\">\n <span>Expand to see logs (</span>\n @let key = subValuesKey(data, 'sub-values');\n @if (hasCompleteSuccess(data)) {\n <span>all succeeded</span>\n <he-svg-icon class=\"is-ml-1\" name=\"checkmark\" class=\"has-text-success\" />\n } @else {\n <span>some failed</span>\n <he-svg-icon class=\"is-ml-1\" name=\"xmark\" class=\"has-text-danger\" />\n }\n <span>)</span>\n </td>\n @for (v of data | repeat: extraColumns; track repeatIndex; let repeatIndex = $index) {\n <td class=\"has-border-right\"></td>\n }\n } @else {\n @for (configModel of methodModelsCount() | times; track configModelIndex; let configModelIndex = $index) {\n <td class=\"has-border-right blank-node-index-{{ configModelIndex }}\">\n @if (getModelsAt(data, configModelIndex); as models) {\n @if ($any(models) | isArray) {\n <div>\n @for (model of $any(models); track model.methodId) {\n <p>\n <ng-container *ngTemplateOutlet=\"blankNodeModel; context: { model, data }\" />\n </p>\n }\n </div>\n } @else {\n <ng-container *ngTemplateOutlet=\"blankNodeModel; context: { model: models, data }\" />\n }\n } @else {\n -\n }\n </td>\n }\n }\n</ng-template>\n\n<ng-template #blankNodeModel let-model=\"model\" let-data=\"data\">\n <div class=\"is-flex is-align-self-stretch is-justify-content-center is-align-items-center is-gap-8\">\n <div class=\"is-flex is-gap-4 is-flex-grow-1 is-align-items-center\">\n <span\n class=\"pl-1 has-text-{{ logColor[model.status] }}\"\n [class.trigger-popover]=\"hasLogs()\"\n [ngbPopover]=\"logStatusDetails\"\n [disablePopover]=\"!hasLogs()\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p1=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"$event.stopPropagation(); p1.isOpen() ? p1.close() : p1.open({ model, data })\">\n <he-svg-icon [name]=\"logIcon[model.status]\" />\n </span>\n\n <span class=\"is-flex is-flex-grow-1 is-gap-4\">\n <span class=\"is-nowrap is-capitalized\">{{ methodName(model) }}</span>\n\n @if (model.logs?.methodTier || model.model?.methodTier) {\n <span class=\"is-nowrap\">[{{ model.logs?.methodTier || model.model?.methodTier }}]</span>\n }\n </span>\n </div>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (model.showLogs) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"logDetails\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : model.showLogs ? p.open({ model }) : null\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n }\n\n @if (model.model) {\n @if (model.showLogs) {\n <div class=\"vertical-divider\"></div>\n }\n <ng-container *ngTemplateOutlet=\"docsLink; context: { $implicit: model.model }\" />\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #logDetails let-model=\"model\">\n @if (hasContributions()) {\n @if (showContributionsLogs()) {\n <div class=\"has-text-right is-mb-1\">\n <button class=\"button is-white is-small is-outlined\" (click)=\"showContributionsLogs.set(false)\">\n Hide contributions details\n </button>\n </div>\n <he-node-logs-models-contributions [node]=\"node()\" [nodeKey]=\"nodeKey()\" [model]=\"model\" />\n } @else {\n <div class=\"has-text-right is-mb-1\">\n <button class=\"button is-white is-small is-outlined\" (click)=\"showContributionsLogs.set(true)\">\n Show contributions details\n </button>\n </div>\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n }\n } @else {\n <he-node-logs-models-logs [logs]=\"model.logs\" />\n }\n</ng-template>\n\n<ng-template #logStatusDetails let-model=\"model\" let-data=\"data\">\n <he-node-logs-models-logs-status [nodeType]=\"nodeType()\" [model]=\"model\" [data]=\"\" />\n</ng-template>\n\n<ng-template #docsLink let-model>\n @if (guideEnabled && model.guidePath) {\n <he-guide-overlay [pageId]=\"model.guidePath\" [width]=\"500\" />\n } @else {\n <a [href]=\"model.docPath || model.path\" target=\"_blank\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .status-legend{border:1px solid #dbe3ea;background:#f5f7f9}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row td:first-child{padding-left:12px}::ng-deep .table .is-sub-row .sub-sub-row-icon{display:none}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table .is-sub-sub-row .sub-sub-row-icon{display:inline-block}::ng-deep .table .is-sub-sub-row .sub-sub-row-icon+div{padding-left:0!important}::ng-deep .table .popover-body .table-container{max-height:260px;overflow-y:auto}\n"] }]
10414
+ }], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }], nodeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKey", required: false }] }], originalValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "originalValues", required: false }] }], recalculatedValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "recalculatedValues", required: false }] }], terms: [{ type: i0.Input, args: [{ isSignal: true, alias: "terms", required: false }] }], filterTermTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterTermTypes", required: false }] }], filterTermTypesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterTermTypesLabel", required: false }] }], logsKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "logsKey", required: false }] }], noDataMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "noDataMessage", required: false }] }], cycle: [{ type: i0.Input, args: [{ isSignal: true, alias: "cycle", required: false }] }], jlogParentKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "jlogParentKey", required: false }] }], jlogParentIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "jlogParentIndex", required: false }] }] } });
9495
10415
 
9496
10416
  var View$4;
9497
10417
  (function (View) {
@@ -9561,7 +10481,7 @@ class CyclesCompletenessComponent {
9561
10481
  component.headerKeys.set(headerKeys$1);
9562
10482
  }
9563
10483
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CyclesCompletenessComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9564
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CyclesCompletenessComponent, isStandalone: true, selector: "he-cycles-completeness", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@switch (selectedView()) {\n @case (View.table) {\n @if (hasData()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (completeness of completenessKeys(); track completeness) {\n <th [attr.title]=\"completeness\">\n <a [href]=\"schemaBaseUrl + '/Completeness#' + completeness\" target=\"_blank\">\n {{ keyToLabel(completeness) }}\n </a>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycle\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n @for (key of completenessKeys(); track key) {\n <td class=\"is-nowrap\">\n <span>\n {{ getCompleteness(cycle)[key] ? 'Complete' : 'Incomplete' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"getCompleteness(cycle)\"\n [key]=\"key\" />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <he-blank-node-state-notice class=\"is-mt-2\" [dataState]=\"dataState()\" />\n } @else {\n <div class=\"panel-block\">\n <span>No completeness data</span>\n </div>\n }\n }\n @case (View.logs) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedNode()) {\n <he-node-logs-models\n class=\"is-mt-2\"\n [node]=\"selectedNode()\"\n [nodeKey]=\"nodeKey\"\n [logsKey]=\"selectedLogsKey()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\" />\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10484
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CyclesCompletenessComponent, isStandalone: true, selector: "he-cycles-completeness", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@switch (selectedView()) {\n @case (View.table) {\n @if (hasData()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (completeness of completenessKeys(); track completeness) {\n <th [attr.title]=\"completeness\">\n <a [href]=\"schemaBaseUrl + '/Completeness#' + completeness\" target=\"_blank\">\n {{ keyToLabel(completeness) }}\n </a>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycle\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n @for (key of completenessKeys(); track key) {\n <td class=\"is-nowrap\">\n <span>\n {{ getCompleteness(cycle)[key] ? 'Complete' : 'Incomplete' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"getCompleteness(cycle)\"\n [key]=\"key\" />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <he-blank-node-state-notice class=\"is-mt-2\" [dataState]=\"dataState()\" />\n } @else {\n <div class=\"panel-block\">\n <span>No completeness data</span>\n </div>\n }\n }\n @case (View.logs) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedNode()) {\n <he-node-logs-models\n class=\"is-mt-2\"\n [node]=\"selectedNode()\"\n [nodeKey]=\"nodeKey\"\n [logsKey]=\"selectedLogsKey()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\" />\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n } @else if (selectedView() === View.logs) {\n <ng-container *ngTemplateOutlet=\"selectCycle\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectCycle>\n @if (cycles().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectCycle\">Cycle</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" id=\"selectCycle\">\n @for (value of cycles(); track value; let cycleIndex = $index) {\n <option [value]=\"cycleIndex\">{{ cycleIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n", styles: [""], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle", "jlogParentKey", "jlogParentIndex"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9565
10485
  }
9566
10486
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CyclesCompletenessComponent, decorators: [{
9567
10487
  type: Component$1,
@@ -9573,7 +10493,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
9573
10493
  BlankNodeStateComponent,
9574
10494
  BlankNodeStateNoticeComponent,
9575
10495
  NodeLogsModelsComponent
9576
- ], template: "@switch (selectedView()) {\n @case (View.table) {\n @if (hasData()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (completeness of completenessKeys(); track completeness) {\n <th [attr.title]=\"completeness\">\n <a [href]=\"schemaBaseUrl + '/Completeness#' + completeness\" target=\"_blank\">\n {{ keyToLabel(completeness) }}\n </a>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycle\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n @for (key of completenessKeys(); track key) {\n <td class=\"is-nowrap\">\n <span>\n {{ getCompleteness(cycle)[key] ? 'Complete' : 'Incomplete' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"getCompleteness(cycle)\"\n [key]=\"key\" />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <he-blank-node-state-notice class=\"is-mt-2\" [dataState]=\"dataState()\" />\n } @else {\n <div class=\"panel-block\">\n <span>No completeness data</span>\n </div>\n }\n }\n @case (View.logs) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedNode()) {\n <he-node-logs-models\n class=\"is-mt-2\"\n [node]=\"selectedNode()\"\n [nodeKey]=\"nodeKey\"\n [logsKey]=\"selectedLogsKey()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\" />\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n" }]
10496
+ ], template: "@switch (selectedView()) {\n @case (View.table) {\n @if (hasData()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (completeness of completenessKeys(); track completeness) {\n <th [attr.title]=\"completeness\">\n <a [href]=\"schemaBaseUrl + '/Completeness#' + completeness\" target=\"_blank\">\n {{ keyToLabel(completeness) }}\n </a>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycle\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n @for (key of completenessKeys(); track key) {\n <td class=\"is-nowrap\">\n <span>\n {{ getCompleteness(cycle)[key] ? 'Complete' : 'Incomplete' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"getCompleteness(cycle)\"\n [key]=\"key\" />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <he-blank-node-state-notice class=\"is-mt-2\" [dataState]=\"dataState()\" />\n } @else {\n <div class=\"panel-block\">\n <span>No completeness data</span>\n </div>\n }\n }\n @case (View.logs) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedNode()) {\n <he-node-logs-models\n class=\"is-mt-2\"\n [node]=\"selectedNode()\"\n [nodeKey]=\"nodeKey\"\n [logsKey]=\"selectedLogsKey()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\" />\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n } @else if (selectedView() === View.logs) {\n <ng-container *ngTemplateOutlet=\"selectCycle\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectCycle>\n @if (cycles().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectCycle\">Cycle</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" id=\"selectCycle\">\n @for (value of cycles(); track value; let cycleIndex = $index) {\n <option [value]=\"cycleIndex\">{{ cycleIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n" }]
9577
10497
  }], ctorParameters: () => [], propDecorators: { dataState: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataState", required: false }] }] } });
9578
10498
 
9579
10499
  const cycleValue = (cycle, values) => (values[cycle['@id']]?.nodes[0] || { value: [0] }).value;
@@ -9698,7 +10618,7 @@ class CyclesMetadataComponent {
9698
10618
  component.headerKeys.set(headerKeys);
9699
10619
  }
9700
10620
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CyclesMetadataComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9701
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CyclesMetadataComponent, isStandalone: true, selector: "he-cycles-metadata", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@switch (selectedView()) {\n @case (View.table) {\n @if (hasData()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (key of keys(); track key) {\n <th [attr.title]=\"key\">\n <a [href]=\"schemaBaseUrl + '/Cycle#' + key\" target=\"_blank\">\n {{ keyToLabel(key) }}\n </a>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycle\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n @for (key of keys(); track key) {\n <td class=\"is-nowrap\">\n @if (isUndefined(cycle[key])) {\n <span>-</span>\n } @else {\n <span>{{ toString(cycle[key]) }}</span>\n }\n <he-blank-node-state class=\"ml-1\" [dataState]=\"dataState()\" [node]=\"cycle\" [key]=\"key\" />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <he-blank-node-state-notice class=\"is-mt-2\" [dataState]=\"dataState()\" />\n } @else {\n <div class=\"panel-block\">\n <span>No data.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.logs) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedCycle()) {\n <he-node-logs-models\n class=\"is-mt-2\"\n [node]=\"selectedCycle()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\" />\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n } @else if (showSelectCycle()) {\n <ng-container *ngTemplateOutlet=\"selectCycle\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectCycle>\n @if (cycles().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectCycle\">Cycle</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectCycle\">\n @for (value of cycles(); track value; let cycleIndex = $index) {\n <option [value]=\"cycleIndex\">{{ cycleIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n", styles: [""], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10621
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CyclesMetadataComponent, isStandalone: true, selector: "he-cycles-metadata", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@switch (selectedView()) {\n @case (View.table) {\n @if (hasData()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (key of keys(); track key) {\n <th [attr.title]=\"key\">\n <a [href]=\"schemaBaseUrl + '/Cycle#' + key\" target=\"_blank\">\n {{ keyToLabel(key) }}\n </a>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycle\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n @for (key of keys(); track key) {\n <td class=\"is-nowrap\">\n @if (isUndefined(cycle[key])) {\n <span>-</span>\n } @else {\n <span>{{ toString(cycle[key]) }}</span>\n }\n <he-blank-node-state class=\"ml-1\" [dataState]=\"dataState()\" [node]=\"cycle\" [key]=\"key\" />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <he-blank-node-state-notice class=\"is-mt-2\" [dataState]=\"dataState()\" />\n } @else {\n <div class=\"panel-block\">\n <span>No data.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.logs) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedCycle()) {\n <he-node-logs-models\n class=\"is-mt-2\"\n [node]=\"selectedCycle()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\" />\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n } @else if (showSelectCycle()) {\n <ng-container *ngTemplateOutlet=\"selectCycle\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectCycle>\n @if (cycles().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectCycle\">Cycle</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectCycle\">\n @for (value of cycles(); track value; let cycleIndex = $index) {\n <option [value]=\"cycleIndex\">{{ cycleIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n", styles: [""], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle", "jlogParentKey", "jlogParentIndex"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9702
10622
  }
9703
10623
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CyclesMetadataComponent, decorators: [{
9704
10624
  type: Component$1,
@@ -10105,6 +11025,18 @@ class CyclesNodesComponent {
10105
11025
  this.isNodeKeyAllowed = computed(() => nodeKeyViews$2[this.firstNodeKey()].includes(this.selectedView()), ...(ngDevMode ? [{ debugName: "isNodeKeyAllowed" }] : []));
10106
11026
  this.groupedNodes = computed(() => this.originalNodes().flatMap(cycle => cycleGroupNodes(this.nodeKeyGroup(), cycle)), ...(ngDevMode ? [{ debugName: "groupedNodes" }] : []));
10107
11027
  this.groupNodeKey = computed(() => groupNodesKeyToProperty[this.nodeKeyGroup()], ...(ngDevMode ? [{ debugName: "groupNodeKey" }] : []));
11028
+ // for a grouped view (e.g. animals), the selected node's index within its parent cycle, so the `.jlog`
11029
+ // can be scoped to `jlog[<group>][<index>]` (the sub-node follows the recalculated node representation)
11030
+ this.jlogParentIndex = computed(() => {
11031
+ const groupKey = this.nodeKeyGroup();
11032
+ const node = this.selectedCycle();
11033
+ if (!groupKey || !node)
11034
+ return undefined;
11035
+ const idKey = this.groupNodeKey();
11036
+ const cycle = this.currentNodes().find(c => c['@id'] === node['@id']);
11037
+ const index = (cycle?.[groupKey] || []).findIndex(n => n?.[idKey] === node[idKey]);
11038
+ return index >= 0 ? index : undefined;
11039
+ }, ...(ngDevMode ? [{ debugName: "jlogParentIndex" }] : []));
10108
11040
  this.groupNodeValues = computed(() => unique(this.groupedNodes().map(value => value[this.groupNodeKey()])), ...(ngDevMode ? [{ debugName: "groupNodeValues" }] : []));
10109
11041
  this.isGroupNode = computed(() => this.nodeKeyGroup() && this.groupedNodes()?.length > 0, ...(ngDevMode ? [{ debugName: "isGroupNode" }] : []));
10110
11042
  this.isEmission = computed(() => this.selectedNodeKey() === BlankNodesKey.emissions, ...(ngDevMode ? [{ debugName: "isEmission" }] : []));
@@ -10136,6 +11068,13 @@ class CyclesNodesComponent {
10136
11068
  this.selectedGroup.set(this.groupNodeValues()[0]);
10137
11069
  }
10138
11070
  });
11071
+ effect(() => {
11072
+ // keep the selected cycle index in range when the available cycles change (e.g. switching group),
11073
+ // otherwise the selected node becomes undefined and the view goes blank
11074
+ if (this.cycles().length > 0 && this.selectedIndex() >= this.cycles().length) {
11075
+ this.selectedIndex.set(0);
11076
+ }
11077
+ });
10139
11078
  }
10140
11079
  groupNodesByKey({ nodeKey }) {
10141
11080
  const nodesPerCycle = groupNodesByTerm(this.cycles(), nodeKey, filterBlankNode$1(this.filterTerm()), this.hideZeroValues(), this.hideIdenticalValues());
@@ -10165,7 +11104,7 @@ class CyclesNodesComponent {
10165
11104
  component.headerKeys.set(this.headerKeys());
10166
11105
  }
10167
11106
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CyclesNodesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10168
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CyclesNodesComponent, isStandalone: true, selector: "he-cycles-nodes", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null }, nodeKeys: { classPropertyName: "nodeKeys", publicName: "nodeKeys", isSignal: true, isRequired: true, transformFunction: null }, nodeKeyGroup: { classPropertyName: "nodeKeyGroup", publicName: "nodeKeyGroup", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isGroupNode()) {\n <div class=\"tabs is-mb-1 | group-nodes-tabs\">\n <ul>\n @for (value of groupNodeValues(); track value) {\n <li [class.is-active]=\"selectedGroup() === value\">\n <a (click)=\"selectedGroup.set(value)\">\n <span class=\"is-capitalized is-pr-1\">{{ nodeKeyGroup() | pluralize: 1 }}:</span>\n <span>{{ value }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n}\n\n@if (isNodeKeyAllowed()) {\n @switch (selectedView()) {\n @case (View.table) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (hasData()) {\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n @if (dataKeys().length > 1) {\n <tr class=\"has-text-weight-bold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @let blankNodesCount = countGroupVisibleNodes(blankNodes);\n @if (blankNodesCount > 0) {\n <th [attr.colspan]=\"blankNodesCount\" [class.has-border-right]=\"!dataKeyLast\">\n <span>{{ dataKey | keyToLabel }}</span>\n </th>\n }\n }\n </tr>\n }\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.name\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <he-node-link [node]=\"node.value.term\">\n <span\n [innerHtml]=\"\n node.value.term.name | ellipsis: 30 | compound: node.value.term.termType\n \"></span>\n </he-node-link>\n </th>\n }\n }\n }\n </tr>\n <tr class=\"is-italic has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <a [href]=\"schemaBaseUrl + '/Cycle#functionalUnit'\" target=\"_blank\">Functional unit</a>\n </th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.units\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <span [innerHtml]=\"node.value.term.units | compound\"></span>\n <he-terms-units-description class=\"is-inline-block is-ml-2\" [term]=\"node.value.term\" />\n </th>\n }\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycleNode(cycle)\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n <td class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <he-cycles-functional-unit-measure [cycle]=\"cycle\" />\n </td>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n @let cycleData = node.value.values[cycle['@id']];\n <td\n class=\"is-nowrap\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n @if (cycleData) {\n <span\n class=\"trigger-popover\"\n [ngbPopover]=\"details\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\"\n [popoverContext]=\"{ data: cycleData, cycle, key: dataKey }\">\n <span pointer>\n {{ cycleData.propertyValue | precision: 3 | default: '-' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"cycleData.node\"\n key=\"value\" />\n </span>\n } @else {\n <span>-</span>\n }\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-justify-content-space-between is-mt-2\">\n <he-blank-node-state-notice\n [dataState]=\"dataState()\"\n [showDeleted]=\"firstNodeKey() === BlankNodesKey.emissions\" />\n <div class=\"is-flex is-flex-wrap-wrap is-gap-8\">\n @if (showHideZeroValues()) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideZeroValues'\"\n name=\"hideZeroValues\"\n [(ngModel)]=\"hideZeroValues\" />\n <label [for]=\"componentId() + 'hideZeroValues'\">\n <span>\n Hide\n <b>0</b>\n values\n </span>\n </label>\n </div>\n }\n @if (cycles().length > 1) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideIdenticalValues'\"\n name=\"hideIdenticalValues\"\n [(ngModel)]=\"hideIdenticalValues\" />\n <label [for]=\"componentId() + 'hideIdenticalValues'\">\n <span>Hide identical values</span>\n </label>\n </div>\n }\n </div>\n </div>\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No data available</span>\n @if (filterTerm()) {\n <span class=\"is-pl-1\">matching your search criteria</span>\n }\n <span>.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.chart) {\n @switch (firstNodeKey()) {\n @case (BlankNodesKey.emissions) {\n <he-cycles-emissions-chart [cycles]=\"cycles()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-emissions-chart>\n }\n }\n }\n @case (View.timeline) {\n <he-cycles-nodes-timeline\n [values]=\"timelineValues()\"\n [minDate]=\"selectedCycle().startDate\"\n [maxDate]=\"selectedCycle().endDate\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-nodes-timeline>\n }\n @case (View.logs) {\n @if (selectedNode()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-node-logs-models\n [node]=\"selectedNode()\"\n [cycle]=\"selectedNode()\"\n [nodeKey]=\"selectedNodeKey()\"\n [logsKey]=\"selectedLogsKey()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\"\n [filterTermTypes]=\"filterTermTypes()\">\n @if (nodeKeys().length > 1) {\n <div class=\"tabs is-m-0\">\n <ul>\n @for (nodeKey of nodeKeys(); track nodeKey) {\n <li [class.is-active]=\"selectedNodeKey() === nodeKey\">\n <a (click)=\"selectedNodeKey.set(nodeKey)\">{{ nodeKey | keyToLabel }}</a>\n </li>\n }\n </ul>\n </div>\n }\n </he-node-logs-models>\n }\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost is-p-2\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n <he-search-extend\n class=\"is-secondary\"\n collapsedClass=\"is-p-2\"\n placeholder=\"Filter terms by name\"\n (searchText)=\"filterTerm.set($event)\" />\n } @else if (showSelectCycle()) {\n <ng-container *ngTemplateOutlet=\"selectCycle\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectCycle>\n @if (cycles().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectCycle\">Cycle</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectCycle\">\n @for (value of cycles(); track value; let cycleIndex = $index) {\n <option [value]=\"cycleIndex\">{{ cycleIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #details let-node=\"cycle\" let-data=\"data\" let-key=\"key\">\n <p>\n <b>{{ defaultLabel(node) }}</b>\n </p>\n <he-node-value-details\n class=\"is-overflow-visible\"\n [data]=\"data\"\n [dataState]=\"dataState()\"\n [nodeType]=\"node['@type']\"\n [dataKey]=\"key\"\n [aggregated]=\"node.aggregated\" />\n</ng-template>\n", styles: [":host{display:block}he-data-table ::ng-deep .table thead tr th:nth-child(2),he-data-table ::ng-deep .table tbody tr td:nth-child(2){max-width:106px;width:106px}\n"], dependencies: [{ kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: SearchExtendComponent, selector: "he-search-extend", inputs: ["value", "disabled", "placeholder", "class", "collapsedClass"], outputs: ["valueChange", "searchText"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: TermsUnitsDescriptionComponent, selector: "he-terms-units-description", inputs: ["term", "iconTemplate"] }, { kind: "component", type: CyclesFunctionalUnitMeasureComponent, selector: "he-cycles-functional-unit-measure", inputs: ["cycle"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "component", type: CyclesEmissionsChartComponent, selector: "he-cycles-emissions-chart", inputs: ["cycles"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CyclesNodesTimelineComponent, selector: "he-cycles-nodes-timeline", inputs: ["values", "maxDate", "minDate"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle"] }, { kind: "component", type: NodeValueDetailsComponent, selector: "he-node-value-details", inputs: ["data", "nodeType", "dataState", "dataKey", "aggregated"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: EllipsisPipe, name: "ellipsis" }, { kind: "pipe", type: KeyToLabelPipe, name: "keyToLabel" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: PluralizePipe, name: "pluralize" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11107
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CyclesNodesComponent, isStandalone: true, selector: "he-cycles-nodes", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null }, nodeKeys: { classPropertyName: "nodeKeys", publicName: "nodeKeys", isSignal: true, isRequired: true, transformFunction: null }, nodeKeyGroup: { classPropertyName: "nodeKeyGroup", publicName: "nodeKeyGroup", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isGroupNode()) {\n <div class=\"tabs is-mb-1 | group-nodes-tabs\">\n <ul>\n @for (value of groupNodeValues(); track value) {\n <li [class.is-active]=\"selectedGroup() === value\">\n <a (click)=\"selectedGroup.set(value)\">\n <span class=\"is-capitalized is-pr-1\">{{ nodeKeyGroup() | pluralize: 1 }}:</span>\n <span>{{ value }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n}\n\n@if (isNodeKeyAllowed()) {\n @switch (selectedView()) {\n @case (View.table) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (hasData()) {\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n @if (dataKeys().length > 1) {\n <tr class=\"has-text-weight-bold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @let blankNodesCount = countGroupVisibleNodes(blankNodes);\n @if (blankNodesCount > 0) {\n <th [attr.colspan]=\"blankNodesCount\" [class.has-border-right]=\"!dataKeyLast\">\n <span>{{ dataKey | keyToLabel }}</span>\n </th>\n }\n }\n </tr>\n }\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.name\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <he-node-link [node]=\"node.value.term\">\n <span\n [innerHtml]=\"\n node.value.term.name | ellipsis: 30 | compound: node.value.term.termType\n \"></span>\n </he-node-link>\n </th>\n }\n }\n }\n </tr>\n <tr class=\"is-italic has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <a [href]=\"schemaBaseUrl + '/Cycle#functionalUnit'\" target=\"_blank\">Functional unit</a>\n </th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.units\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <span [innerHtml]=\"node.value.term.units | compound\"></span>\n <he-terms-units-description class=\"is-inline-block is-ml-2\" [term]=\"node.value.term\" />\n </th>\n }\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycleNode(cycle)\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n <td class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <he-cycles-functional-unit-measure [cycle]=\"cycle\" />\n </td>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n @let cycleData = node.value.values[cycle['@id']];\n <td\n class=\"is-nowrap\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n @if (cycleData) {\n <span\n class=\"trigger-popover\"\n [ngbPopover]=\"details\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\"\n [popoverContext]=\"{ data: cycleData, cycle, key: dataKey }\">\n <span pointer>\n {{ cycleData.propertyValue | precision: 3 | default: '-' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"cycleData.node\"\n key=\"value\" />\n </span>\n } @else {\n <span>-</span>\n }\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-justify-content-space-between is-mt-2\">\n <he-blank-node-state-notice\n [dataState]=\"dataState()\"\n [showDeleted]=\"firstNodeKey() === BlankNodesKey.emissions\" />\n <div class=\"is-flex is-flex-wrap-wrap is-gap-8\">\n @if (showHideZeroValues()) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideZeroValues'\"\n name=\"hideZeroValues\"\n [(ngModel)]=\"hideZeroValues\" />\n <label [for]=\"componentId() + 'hideZeroValues'\">\n <span>\n Hide\n <b>0</b>\n values\n </span>\n </label>\n </div>\n }\n @if (cycles().length > 1) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideIdenticalValues'\"\n name=\"hideIdenticalValues\"\n [(ngModel)]=\"hideIdenticalValues\" />\n <label [for]=\"componentId() + 'hideIdenticalValues'\">\n <span>Hide identical values</span>\n </label>\n </div>\n }\n </div>\n </div>\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No data available</span>\n @if (filterTerm()) {\n <span class=\"is-pl-1\">matching your search criteria</span>\n }\n <span>.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.chart) {\n @switch (firstNodeKey()) {\n @case (BlankNodesKey.emissions) {\n <he-cycles-emissions-chart [cycles]=\"cycles()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-emissions-chart>\n }\n }\n }\n @case (View.timeline) {\n <he-cycles-nodes-timeline\n [values]=\"timelineValues()\"\n [minDate]=\"selectedCycle().startDate\"\n [maxDate]=\"selectedCycle().endDate\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-nodes-timeline>\n }\n @case (View.logs) {\n <!-- keep the cycle selector visible even when the selected cycle has no grouped node (e.g. a cycle\n with no transformations under \"Transformation: Inputs\"), so the user can switch to one that has -->\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedNode()) {\n <he-node-logs-models\n [node]=\"selectedNode()\"\n [cycle]=\"selectedNode()\"\n [nodeKey]=\"selectedNodeKey()\"\n [logsKey]=\"selectedLogsKey()\"\n [jlogParentKey]=\"nodeKeyGroup()\"\n [jlogParentIndex]=\"jlogParentIndex()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\"\n [filterTermTypes]=\"filterTermTypes()\">\n @if (nodeKeys().length > 1) {\n <div class=\"tabs is-m-0\">\n <ul>\n @for (nodeKey of nodeKeys(); track nodeKey) {\n <li [class.is-active]=\"selectedNodeKey() === nodeKey\">\n <a (click)=\"selectedNodeKey.set(nodeKey)\">{{ nodeKey | keyToLabel }}</a>\n </li>\n }\n </ul>\n </div>\n }\n </he-node-logs-models>\n } @else if (isGroupNode()) {\n <p class=\"is-p-3 has-text-grey\">\n No recalculation logs for the selected {{ nodeKeyGroup() | pluralize: 1 }} in this cycle \u2014 select\n another cycle above.\n </p>\n }\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost is-p-2\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n <he-search-extend\n class=\"is-secondary\"\n collapsedClass=\"is-p-2\"\n placeholder=\"Filter terms by name\"\n (searchText)=\"filterTerm.set($event)\" />\n } @else if (showSelectCycle()) {\n <ng-container *ngTemplateOutlet=\"selectCycle\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectCycle>\n @if (cycles().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectCycle\">Cycle</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectCycle\">\n @for (value of cycles(); track value; let cycleIndex = $index) {\n <option [value]=\"cycleIndex\">{{ cycleIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #details let-node=\"cycle\" let-data=\"data\" let-key=\"key\">\n <p>\n <b>{{ defaultLabel(node) }}</b>\n </p>\n <he-node-value-details\n class=\"is-overflow-visible\"\n [data]=\"data\"\n [dataState]=\"dataState()\"\n [nodeType]=\"node['@type']\"\n [dataKey]=\"key\"\n [aggregated]=\"node.aggregated\" />\n</ng-template>\n", styles: [":host{display:block}he-data-table ::ng-deep .table thead tr th:nth-child(2),he-data-table ::ng-deep .table tbody tr td:nth-child(2){max-width:106px;width:106px}\n"], dependencies: [{ kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: SearchExtendComponent, selector: "he-search-extend", inputs: ["value", "disabled", "placeholder", "class", "collapsedClass"], outputs: ["valueChange", "searchText"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: TermsUnitsDescriptionComponent, selector: "he-terms-units-description", inputs: ["term", "iconTemplate"] }, { kind: "component", type: CyclesFunctionalUnitMeasureComponent, selector: "he-cycles-functional-unit-measure", inputs: ["cycle"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "component", type: CyclesEmissionsChartComponent, selector: "he-cycles-emissions-chart", inputs: ["cycles"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CyclesNodesTimelineComponent, selector: "he-cycles-nodes-timeline", inputs: ["values", "maxDate", "minDate"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle", "jlogParentKey", "jlogParentIndex"] }, { kind: "component", type: NodeValueDetailsComponent, selector: "he-node-value-details", inputs: ["data", "nodeType", "dataState", "dataKey", "aggregated"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: EllipsisPipe, name: "ellipsis" }, { kind: "pipe", type: KeyToLabelPipe, name: "keyToLabel" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: PluralizePipe, name: "pluralize" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10169
11108
  }
10170
11109
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CyclesNodesComponent, decorators: [{
10171
11110
  type: Component$1,
@@ -10191,7 +11130,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
10191
11130
  KeyToLabelPipe,
10192
11131
  PrecisionPipe,
10193
11132
  PluralizePipe
10194
- ], template: "@if (isGroupNode()) {\n <div class=\"tabs is-mb-1 | group-nodes-tabs\">\n <ul>\n @for (value of groupNodeValues(); track value) {\n <li [class.is-active]=\"selectedGroup() === value\">\n <a (click)=\"selectedGroup.set(value)\">\n <span class=\"is-capitalized is-pr-1\">{{ nodeKeyGroup() | pluralize: 1 }}:</span>\n <span>{{ value }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n}\n\n@if (isNodeKeyAllowed()) {\n @switch (selectedView()) {\n @case (View.table) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (hasData()) {\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n @if (dataKeys().length > 1) {\n <tr class=\"has-text-weight-bold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @let blankNodesCount = countGroupVisibleNodes(blankNodes);\n @if (blankNodesCount > 0) {\n <th [attr.colspan]=\"blankNodesCount\" [class.has-border-right]=\"!dataKeyLast\">\n <span>{{ dataKey | keyToLabel }}</span>\n </th>\n }\n }\n </tr>\n }\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.name\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <he-node-link [node]=\"node.value.term\">\n <span\n [innerHtml]=\"\n node.value.term.name | ellipsis: 30 | compound: node.value.term.termType\n \"></span>\n </he-node-link>\n </th>\n }\n }\n }\n </tr>\n <tr class=\"is-italic has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <a [href]=\"schemaBaseUrl + '/Cycle#functionalUnit'\" target=\"_blank\">Functional unit</a>\n </th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.units\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <span [innerHtml]=\"node.value.term.units | compound\"></span>\n <he-terms-units-description class=\"is-inline-block is-ml-2\" [term]=\"node.value.term\" />\n </th>\n }\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycleNode(cycle)\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n <td class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <he-cycles-functional-unit-measure [cycle]=\"cycle\" />\n </td>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n @let cycleData = node.value.values[cycle['@id']];\n <td\n class=\"is-nowrap\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n @if (cycleData) {\n <span\n class=\"trigger-popover\"\n [ngbPopover]=\"details\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\"\n [popoverContext]=\"{ data: cycleData, cycle, key: dataKey }\">\n <span pointer>\n {{ cycleData.propertyValue | precision: 3 | default: '-' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"cycleData.node\"\n key=\"value\" />\n </span>\n } @else {\n <span>-</span>\n }\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-justify-content-space-between is-mt-2\">\n <he-blank-node-state-notice\n [dataState]=\"dataState()\"\n [showDeleted]=\"firstNodeKey() === BlankNodesKey.emissions\" />\n <div class=\"is-flex is-flex-wrap-wrap is-gap-8\">\n @if (showHideZeroValues()) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideZeroValues'\"\n name=\"hideZeroValues\"\n [(ngModel)]=\"hideZeroValues\" />\n <label [for]=\"componentId() + 'hideZeroValues'\">\n <span>\n Hide\n <b>0</b>\n values\n </span>\n </label>\n </div>\n }\n @if (cycles().length > 1) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideIdenticalValues'\"\n name=\"hideIdenticalValues\"\n [(ngModel)]=\"hideIdenticalValues\" />\n <label [for]=\"componentId() + 'hideIdenticalValues'\">\n <span>Hide identical values</span>\n </label>\n </div>\n }\n </div>\n </div>\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No data available</span>\n @if (filterTerm()) {\n <span class=\"is-pl-1\">matching your search criteria</span>\n }\n <span>.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.chart) {\n @switch (firstNodeKey()) {\n @case (BlankNodesKey.emissions) {\n <he-cycles-emissions-chart [cycles]=\"cycles()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-emissions-chart>\n }\n }\n }\n @case (View.timeline) {\n <he-cycles-nodes-timeline\n [values]=\"timelineValues()\"\n [minDate]=\"selectedCycle().startDate\"\n [maxDate]=\"selectedCycle().endDate\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-nodes-timeline>\n }\n @case (View.logs) {\n @if (selectedNode()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-node-logs-models\n [node]=\"selectedNode()\"\n [cycle]=\"selectedNode()\"\n [nodeKey]=\"selectedNodeKey()\"\n [logsKey]=\"selectedLogsKey()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\"\n [filterTermTypes]=\"filterTermTypes()\">\n @if (nodeKeys().length > 1) {\n <div class=\"tabs is-m-0\">\n <ul>\n @for (nodeKey of nodeKeys(); track nodeKey) {\n <li [class.is-active]=\"selectedNodeKey() === nodeKey\">\n <a (click)=\"selectedNodeKey.set(nodeKey)\">{{ nodeKey | keyToLabel }}</a>\n </li>\n }\n </ul>\n </div>\n }\n </he-node-logs-models>\n }\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost is-p-2\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n <he-search-extend\n class=\"is-secondary\"\n collapsedClass=\"is-p-2\"\n placeholder=\"Filter terms by name\"\n (searchText)=\"filterTerm.set($event)\" />\n } @else if (showSelectCycle()) {\n <ng-container *ngTemplateOutlet=\"selectCycle\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectCycle>\n @if (cycles().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectCycle\">Cycle</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectCycle\">\n @for (value of cycles(); track value; let cycleIndex = $index) {\n <option [value]=\"cycleIndex\">{{ cycleIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #details let-node=\"cycle\" let-data=\"data\" let-key=\"key\">\n <p>\n <b>{{ defaultLabel(node) }}</b>\n </p>\n <he-node-value-details\n class=\"is-overflow-visible\"\n [data]=\"data\"\n [dataState]=\"dataState()\"\n [nodeType]=\"node['@type']\"\n [dataKey]=\"key\"\n [aggregated]=\"node.aggregated\" />\n</ng-template>\n", styles: [":host{display:block}he-data-table ::ng-deep .table thead tr th:nth-child(2),he-data-table ::ng-deep .table tbody tr td:nth-child(2){max-width:106px;width:106px}\n"] }]
11133
+ ], template: "@if (isGroupNode()) {\n <div class=\"tabs is-mb-1 | group-nodes-tabs\">\n <ul>\n @for (value of groupNodeValues(); track value) {\n <li [class.is-active]=\"selectedGroup() === value\">\n <a (click)=\"selectedGroup.set(value)\">\n <span class=\"is-capitalized is-pr-1\">{{ nodeKeyGroup() | pluralize: 1 }}:</span>\n <span>{{ value }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n}\n\n@if (isNodeKeyAllowed()) {\n @switch (selectedView()) {\n @case (View.table) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (hasData()) {\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n @if (dataKeys().length > 1) {\n <tr class=\"has-text-weight-bold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @let blankNodesCount = countGroupVisibleNodes(blankNodes);\n @if (blankNodesCount > 0) {\n <th [attr.colspan]=\"blankNodesCount\" [class.has-border-right]=\"!dataKeyLast\">\n <span>{{ dataKey | keyToLabel }}</span>\n </th>\n }\n }\n </tr>\n }\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.name\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <he-node-link [node]=\"node.value.term\">\n <span\n [innerHtml]=\"\n node.value.term.name | ellipsis: 30 | compound: node.value.term.termType\n \"></span>\n </he-node-link>\n </th>\n }\n }\n }\n </tr>\n <tr class=\"is-italic has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <a [href]=\"schemaBaseUrl + '/Cycle#functionalUnit'\" target=\"_blank\">Functional unit</a>\n </th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.units\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <span [innerHtml]=\"node.value.term.units | compound\"></span>\n <he-terms-units-description class=\"is-inline-block is-ml-2\" [term]=\"node.value.term\" />\n </th>\n }\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track cycle['@id']; let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycleNode(cycle)\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n <td class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <he-cycles-functional-unit-measure [cycle]=\"cycle\" />\n </td>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n @let cycleData = node.value.values[cycle['@id']];\n <td\n class=\"is-nowrap\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n @if (cycleData) {\n <span\n class=\"trigger-popover\"\n [ngbPopover]=\"details\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\"\n [popoverContext]=\"{ data: cycleData, cycle, key: dataKey }\">\n <span pointer>\n {{ cycleData.propertyValue | precision: 3 | default: '-' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"cycleData.node\"\n key=\"value\" />\n </span>\n } @else {\n <span>-</span>\n }\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-justify-content-space-between is-mt-2\">\n <he-blank-node-state-notice\n [dataState]=\"dataState()\"\n [showDeleted]=\"firstNodeKey() === BlankNodesKey.emissions\" />\n <div class=\"is-flex is-flex-wrap-wrap is-gap-8\">\n @if (showHideZeroValues()) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideZeroValues'\"\n name=\"hideZeroValues\"\n [(ngModel)]=\"hideZeroValues\" />\n <label [for]=\"componentId() + 'hideZeroValues'\">\n <span>\n Hide\n <b>0</b>\n values\n </span>\n </label>\n </div>\n }\n @if (cycles().length > 1) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideIdenticalValues'\"\n name=\"hideIdenticalValues\"\n [(ngModel)]=\"hideIdenticalValues\" />\n <label [for]=\"componentId() + 'hideIdenticalValues'\">\n <span>Hide identical values</span>\n </label>\n </div>\n }\n </div>\n </div>\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No data available</span>\n @if (filterTerm()) {\n <span class=\"is-pl-1\">matching your search criteria</span>\n }\n <span>.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.chart) {\n @switch (firstNodeKey()) {\n @case (BlankNodesKey.emissions) {\n <he-cycles-emissions-chart [cycles]=\"cycles()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-emissions-chart>\n }\n }\n }\n @case (View.timeline) {\n <he-cycles-nodes-timeline\n [values]=\"timelineValues()\"\n [minDate]=\"selectedCycle().startDate\"\n [maxDate]=\"selectedCycle().endDate\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-nodes-timeline>\n }\n @case (View.logs) {\n <!-- keep the cycle selector visible even when the selected cycle has no grouped node (e.g. a cycle\n with no transformations under \"Transformation: Inputs\"), so the user can switch to one that has -->\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedNode()) {\n <he-node-logs-models\n [node]=\"selectedNode()\"\n [cycle]=\"selectedNode()\"\n [nodeKey]=\"selectedNodeKey()\"\n [logsKey]=\"selectedLogsKey()\"\n [jlogParentKey]=\"nodeKeyGroup()\"\n [jlogParentIndex]=\"jlogParentIndex()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\"\n [filterTermTypes]=\"filterTermTypes()\">\n @if (nodeKeys().length > 1) {\n <div class=\"tabs is-m-0\">\n <ul>\n @for (nodeKey of nodeKeys(); track nodeKey) {\n <li [class.is-active]=\"selectedNodeKey() === nodeKey\">\n <a (click)=\"selectedNodeKey.set(nodeKey)\">{{ nodeKey | keyToLabel }}</a>\n </li>\n }\n </ul>\n </div>\n }\n </he-node-logs-models>\n } @else if (isGroupNode()) {\n <p class=\"is-p-3 has-text-grey\">\n No recalculation logs for the selected {{ nodeKeyGroup() | pluralize: 1 }} in this cycle \u2014 select\n another cycle above.\n </p>\n }\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost is-p-2\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n <he-search-extend\n class=\"is-secondary\"\n collapsedClass=\"is-p-2\"\n placeholder=\"Filter terms by name\"\n (searchText)=\"filterTerm.set($event)\" />\n } @else if (showSelectCycle()) {\n <ng-container *ngTemplateOutlet=\"selectCycle\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectCycle>\n @if (cycles().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectCycle\">Cycle</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectCycle\">\n @for (value of cycles(); track value; let cycleIndex = $index) {\n <option [value]=\"cycleIndex\">{{ cycleIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #details let-node=\"cycle\" let-data=\"data\" let-key=\"key\">\n <p>\n <b>{{ defaultLabel(node) }}</b>\n </p>\n <he-node-value-details\n class=\"is-overflow-visible\"\n [data]=\"data\"\n [dataState]=\"dataState()\"\n [nodeType]=\"node['@type']\"\n [dataKey]=\"key\"\n [aggregated]=\"node.aggregated\" />\n</ng-template>\n", styles: [":host{display:block}he-data-table ::ng-deep .table thead tr th:nth-child(2),he-data-table ::ng-deep .table tbody tr td:nth-child(2){max-width:106px;width:106px}\n"] }]
10195
11134
  }], ctorParameters: () => [], propDecorators: { dataState: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataState", required: false }] }], nodeKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKeys", required: true }] }], nodeKeyGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKeyGroup", required: false }] }] } });
10196
11135
 
10197
11136
  class CyclesResultComponent {
@@ -14967,7 +15906,7 @@ class ImpactAssessmentsProductsComponent {
14967
15906
  component.headerKeys.set(this.headerKeys());
14968
15907
  }
14969
15908
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ImpactAssessmentsProductsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
14970
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ImpactAssessmentsProductsComponent, isStandalone: true, selector: "he-impact-assessments-products", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypes: { classPropertyName: "filterTermTypes", publicName: "filterTermTypes", isSignal: true, isRequired: false, transformFunction: null }, enableFilterMethodModel: { classPropertyName: "enableFilterMethodModel", publicName: "enableFilterMethodModel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isNodeKeyAllowed()) {\n @switch (selectedView()) {\n @case (View.table) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <div class=\"is-hidden-tablet is-mt-3\">\n <ng-container *ngTemplateOutlet=\"filterModel\" />\n </div>\n\n @if (hasData()) {\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n @if (dataKeys().length > 1) {\n <tr class=\"has-text-weight-bold\">\n <th class=\"width-auto\"></th>\n <th></th>\n <th></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @let blankNodesCount = countGroupVisibleNodes(blankNodes);\n @if (blankNodesCount > 0) {\n <th [attr.colspan]=\"blankNodesCount\" [class.has-border-right]=\"!dataKeyLast\">\n @if (dataKey) {\n <span>{{ dataKey | keyToLabel }}</span>\n }\n </th>\n }\n }\n </tr>\n }\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right is-py-0\">\n <div class=\"is-hidden-mobile\">\n <ng-container *ngTemplateOutlet=\"filterModel\" />\n </div>\n </th>\n <th class=\"has-border-right\"></th>\n <th class=\"has-border-right\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.name\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <he-node-link [node]=\"node.value.term\">\n <span\n [innerHtml]=\"\n node.value.term.name | ellipsis: 30 | compound: node.value.term.termType\n \"></span>\n </he-node-link>\n </th>\n }\n }\n }\n </tr>\n <tr class=\"is-italic has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\">\n <a [href]=\"schemaBaseUrl + '/ImpactAssessment#functionalUnit'\" target=\"_blank\">Functional unit</a>\n </th>\n <th class=\"has-border-right\">\n <a [href]=\"schemaBaseUrl + '/ImpactAssessment#product'\" target=\"_blank\">Product</a>\n </th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.units\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <span [innerHtml]=\"node.value.term.units | compound\"></span>\n <he-terms-units-description class=\"is-inline-block is-ml-2\" [term]=\"node.value.term\" />\n </th>\n }\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (impactAssessment of impactAssessments(); track impactAssessment['@id']; let impactIndex = $index) {\n @let product = productTerm(impactAssessment);\n <tr [class.is-suggested]=\"$any(impactAssessment).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"impactName(impactAssessment)\">\n <he-node-link [node]=\"impactAssessment\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(impactAssessment).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ impactIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ impactName(impactAssessment) }}</span>\n </span>\n </he-node-link>\n </td>\n <td class=\"has-border-right\" [attr.title]=\"product?.units\">\n <span>1 {{ product?.units }}</span>\n </td>\n <td class=\"has-border-right\" [attr.title]=\"product?.name\">\n @if (product) {\n <he-node-link [node]=\"product\">\n <span>{{ product.name | ellipsis: 30 }}</span>\n </he-node-link>\n }\n </td>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n @let impactData = node.value.values[impactAssessment['@id']];\n <td\n class=\"is-nowrap\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n @if (impactData) {\n <span\n class=\"trigger-popover\"\n [ngbPopover]=\"details\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\"\n [popoverContext]=\"{\n data: impactData,\n impactAssessment\n }\">\n <span pointer>\n {{ impactData.propertyValue | precision: 3 | default: '-' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"impactData.node\"\n key=\"value\"\n [state]=\"impactAssessment.aggregated ? 'aggregated' : undefined\" />\n </span>\n } @else {\n <span>-</span>\n }\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-justify-content-space-between is-mt-2\">\n <he-blank-node-state-notice [dataState]=\"dataState()\" />\n @if (impactAssessments().length > 1) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideIdenticalValues'\"\n name=\"hideIdenticalValues\"\n [(ngModel)]=\"hideIdenticalValues\" />\n <label [for]=\"componentId() + 'hideIdenticalValues'\">\n <span>Hide identical values</span>\n </label>\n </div>\n }\n </div>\n } @else if (noDataMessage()) {\n <div class=\"is-pt-3 has-text-centered\">\n <span>{{ noDataMessage() }}</span>\n </div>\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No data available</span>\n @if (filterTerm()) {\n <span class=\"is-pl-1\">matching your search criteria</span>\n }\n <span>.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.chart) {\n <he-impact-assessments-indicators-chart [key]=\"nodeKey()\" [filterTermTypes]=\"filterTermTypes()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-impact-assessments-indicators-chart>\n }\n @case (View.breakdown) {\n @if (selectedNode()) {\n <he-impact-assessments-indicator-breakdown-chart\n [impactAssessment]=\"selectedNode()\"\n [indicators]=\"selectedRecalculatedValues()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-impact-assessments-indicator-breakdown-chart>\n }\n }\n @case (View.logs) {\n @if (selectedNode()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-node-logs-models\n [node]=\"selectedNode()\"\n [cycle]=\"selectedCycle()\"\n [nodeKey]=\"nodeKey()\"\n [filterTermTypes]=\"filterTermTypes()\"\n [filterTermTypesLabel]=\"filterTermTypesLabel()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\"\n [noDataMessage]=\"noDataMessage()\" />\n }\n }\n }\n}\n\n<ng-template #filterModel>\n @if (enableFilterMethodModel()) {\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"updateSelectedMethodModel($event.target.value)\">\n <option [ngValue]=\"undefined\">Filter Model</option>\n @for (term of methodModels(); track term) {\n <option [ngValue]=\"term\">{{ term.name }}</option>\n }\n </select>\n </div>\n }\n</ng-template>\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost is-p-2\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n <he-search-extend\n class=\"is-secondary\"\n collapsedClass=\"is-p-2\"\n placeholder=\"Filter terms by name\"\n (searchText)=\"filterTerm.set($event)\" />\n } @else if (showSelectImpact()) {\n <ng-container *ngTemplateOutlet=\"selectImpact\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectImpact>\n @if (impactAssessments().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectImpactAssessment\">Impact Assessment</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectImpactAssessment\">\n @for (value of impactAssessments(); track value; let i = $index) {\n <option [value]=\"i\">{{ i + 1 }}. {{ impactName(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #details let-node=\"impactAssessment\" let-data=\"data\">\n <p>\n <b>\n @if (data.cycle) {\n <span>{{ cycleLabel(node.cycle) }}</span>\n }\n @if (!data.cycle) {\n <span>{{ data.name }}</span>\n }\n </b>\n </p>\n <he-node-value-details\n class=\"is-overflow-visible\"\n [data]=\"data\"\n [dataState]=\"dataState()\"\n [nodeType]=\"node['@type']\"\n [dataKey]=\"nodeKey()\"\n [aggregated]=\"node.aggregated\" />\n</ng-template>\n", styles: [":host{display:block}he-data-table ::ng-deep .table thead tr th:nth-child(2),he-data-table ::ng-deep .table tbody tr td:nth-child(2){max-width:102px;width:102px}he-data-table ::ng-deep .table thead tr th:nth-child(3),he-data-table ::ng-deep .table tbody tr td:nth-child(3){min-width:110px}\n"], dependencies: [{ kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: SearchExtendComponent, selector: "he-search-extend", inputs: ["value", "disabled", "placeholder", "class", "collapsedClass"], outputs: ["valueChange", "searchText"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: TermsUnitsDescriptionComponent, selector: "he-terms-units-description", inputs: ["term", "iconTemplate"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "component", type: ImpactAssessmentsIndicatorsChartComponent, selector: "he-impact-assessments-indicators-chart", inputs: ["key", "filterTermTypes"] }, { kind: "component", type: ImpactAssessmentsIndicatorBreakdownChartComponent, selector: "he-impact-assessments-indicator-breakdown-chart", inputs: ["impactAssessment", "indicators"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle"] }, { kind: "component", type: NodeValueDetailsComponent, selector: "he-node-value-details", inputs: ["data", "nodeType", "dataState", "dataKey", "aggregated"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: EllipsisPipe, name: "ellipsis" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: KeyToLabelPipe, name: "keyToLabel" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
15909
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ImpactAssessmentsProductsComponent, isStandalone: true, selector: "he-impact-assessments-products", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypes: { classPropertyName: "filterTermTypes", publicName: "filterTermTypes", isSignal: true, isRequired: false, transformFunction: null }, enableFilterMethodModel: { classPropertyName: "enableFilterMethodModel", publicName: "enableFilterMethodModel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isNodeKeyAllowed()) {\n @switch (selectedView()) {\n @case (View.table) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <div class=\"is-hidden-tablet is-mt-3\">\n <ng-container *ngTemplateOutlet=\"filterModel\" />\n </div>\n\n @if (hasData()) {\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n @if (dataKeys().length > 1) {\n <tr class=\"has-text-weight-bold\">\n <th class=\"width-auto\"></th>\n <th></th>\n <th></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @let blankNodesCount = countGroupVisibleNodes(blankNodes);\n @if (blankNodesCount > 0) {\n <th [attr.colspan]=\"blankNodesCount\" [class.has-border-right]=\"!dataKeyLast\">\n @if (dataKey) {\n <span>{{ dataKey | keyToLabel }}</span>\n }\n </th>\n }\n }\n </tr>\n }\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right is-py-0\">\n <div class=\"is-hidden-mobile\">\n <ng-container *ngTemplateOutlet=\"filterModel\" />\n </div>\n </th>\n <th class=\"has-border-right\"></th>\n <th class=\"has-border-right\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.name\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <he-node-link [node]=\"node.value.term\">\n <span\n [innerHtml]=\"\n node.value.term.name | ellipsis: 30 | compound: node.value.term.termType\n \"></span>\n </he-node-link>\n </th>\n }\n }\n }\n </tr>\n <tr class=\"is-italic has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\">\n <a [href]=\"schemaBaseUrl + '/ImpactAssessment#functionalUnit'\" target=\"_blank\">Functional unit</a>\n </th>\n <th class=\"has-border-right\">\n <a [href]=\"schemaBaseUrl + '/ImpactAssessment#product'\" target=\"_blank\">Product</a>\n </th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.units\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <span [innerHtml]=\"node.value.term.units | compound\"></span>\n <he-terms-units-description class=\"is-inline-block is-ml-2\" [term]=\"node.value.term\" />\n </th>\n }\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (impactAssessment of impactAssessments(); track impactAssessment['@id']; let impactIndex = $index) {\n @let product = productTerm(impactAssessment);\n <tr [class.is-suggested]=\"$any(impactAssessment).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"impactName(impactAssessment)\">\n <he-node-link [node]=\"impactAssessment\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(impactAssessment).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ impactIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ impactName(impactAssessment) }}</span>\n </span>\n </he-node-link>\n </td>\n <td class=\"has-border-right\" [attr.title]=\"product?.units\">\n <span>1 {{ product?.units }}</span>\n </td>\n <td class=\"has-border-right\" [attr.title]=\"product?.name\">\n @if (product) {\n <he-node-link [node]=\"product\">\n <span>{{ product.name | ellipsis: 30 }}</span>\n </he-node-link>\n }\n </td>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n @let impactData = node.value.values[impactAssessment['@id']];\n <td\n class=\"is-nowrap\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n @if (impactData) {\n <span\n class=\"trigger-popover\"\n [ngbPopover]=\"details\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\"\n [popoverContext]=\"{\n data: impactData,\n impactAssessment\n }\">\n <span pointer>\n {{ impactData.propertyValue | precision: 3 | default: '-' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"impactData.node\"\n key=\"value\"\n [state]=\"impactAssessment.aggregated ? 'aggregated' : undefined\" />\n </span>\n } @else {\n <span>-</span>\n }\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-justify-content-space-between is-mt-2\">\n <he-blank-node-state-notice [dataState]=\"dataState()\" />\n @if (impactAssessments().length > 1) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideIdenticalValues'\"\n name=\"hideIdenticalValues\"\n [(ngModel)]=\"hideIdenticalValues\" />\n <label [for]=\"componentId() + 'hideIdenticalValues'\">\n <span>Hide identical values</span>\n </label>\n </div>\n }\n </div>\n } @else if (noDataMessage()) {\n <div class=\"is-pt-3 has-text-centered\">\n <span>{{ noDataMessage() }}</span>\n </div>\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No data available</span>\n @if (filterTerm()) {\n <span class=\"is-pl-1\">matching your search criteria</span>\n }\n <span>.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.chart) {\n <he-impact-assessments-indicators-chart [key]=\"nodeKey()\" [filterTermTypes]=\"filterTermTypes()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-impact-assessments-indicators-chart>\n }\n @case (View.breakdown) {\n @if (selectedNode()) {\n <he-impact-assessments-indicator-breakdown-chart\n [impactAssessment]=\"selectedNode()\"\n [indicators]=\"selectedRecalculatedValues()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-impact-assessments-indicator-breakdown-chart>\n }\n }\n @case (View.logs) {\n @if (selectedNode()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-node-logs-models\n [node]=\"selectedNode()\"\n [cycle]=\"selectedCycle()\"\n [nodeKey]=\"nodeKey()\"\n [filterTermTypes]=\"filterTermTypes()\"\n [filterTermTypesLabel]=\"filterTermTypesLabel()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\"\n [noDataMessage]=\"noDataMessage()\" />\n }\n }\n }\n}\n\n<ng-template #filterModel>\n @if (enableFilterMethodModel()) {\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"updateSelectedMethodModel($event.target.value)\">\n <option [ngValue]=\"undefined\">Filter Model</option>\n @for (term of methodModels(); track term) {\n <option [ngValue]=\"term\">{{ term.name }}</option>\n }\n </select>\n </div>\n }\n</ng-template>\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost is-p-2\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n <he-search-extend\n class=\"is-secondary\"\n collapsedClass=\"is-p-2\"\n placeholder=\"Filter terms by name\"\n (searchText)=\"filterTerm.set($event)\" />\n } @else if (showSelectImpact()) {\n <ng-container *ngTemplateOutlet=\"selectImpact\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectImpact>\n @if (impactAssessments().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectImpactAssessment\">Impact Assessment</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectImpactAssessment\">\n @for (value of impactAssessments(); track value; let i = $index) {\n <option [value]=\"i\">{{ i + 1 }}. {{ impactName(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #details let-node=\"impactAssessment\" let-data=\"data\">\n <p>\n <b>\n @if (data.cycle) {\n <span>{{ cycleLabel(node.cycle) }}</span>\n }\n @if (!data.cycle) {\n <span>{{ data.name }}</span>\n }\n </b>\n </p>\n <he-node-value-details\n class=\"is-overflow-visible\"\n [data]=\"data\"\n [dataState]=\"dataState()\"\n [nodeType]=\"node['@type']\"\n [dataKey]=\"nodeKey()\"\n [aggregated]=\"node.aggregated\" />\n</ng-template>\n", styles: [":host{display:block}he-data-table ::ng-deep .table thead tr th:nth-child(2),he-data-table ::ng-deep .table tbody tr td:nth-child(2){max-width:102px;width:102px}he-data-table ::ng-deep .table thead tr th:nth-child(3),he-data-table ::ng-deep .table tbody tr td:nth-child(3){min-width:110px}\n"], dependencies: [{ kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: SearchExtendComponent, selector: "he-search-extend", inputs: ["value", "disabled", "placeholder", "class", "collapsedClass"], outputs: ["valueChange", "searchText"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: TermsUnitsDescriptionComponent, selector: "he-terms-units-description", inputs: ["term", "iconTemplate"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "component", type: ImpactAssessmentsIndicatorsChartComponent, selector: "he-impact-assessments-indicators-chart", inputs: ["key", "filterTermTypes"] }, { kind: "component", type: ImpactAssessmentsIndicatorBreakdownChartComponent, selector: "he-impact-assessments-indicator-breakdown-chart", inputs: ["impactAssessment", "indicators"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle", "jlogParentKey", "jlogParentIndex"] }, { kind: "component", type: NodeValueDetailsComponent, selector: "he-node-value-details", inputs: ["data", "nodeType", "dataState", "dataKey", "aggregated"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: EllipsisPipe, name: "ellipsis" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: KeyToLabelPipe, name: "keyToLabel" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
14971
15910
  }
14972
15911
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ImpactAssessmentsProductsComponent, decorators: [{
14973
15912
  type: Component$1,
@@ -15559,7 +16498,7 @@ class SitesNodesComponent {
15559
16498
  component.headerKeys.set(this.headerKeys());
15560
16499
  }
15561
16500
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SitesNodesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
15562
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SitesNodesComponent, isStandalone: true, selector: "he-sites-nodes", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, enableChart: { classPropertyName: "enableChart", publicName: "enableChart", isSignal: true, isRequired: false, transformFunction: null }, nodeKeyGroup: { classPropertyName: "nodeKeyGroup", publicName: "nodeKeyGroup", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isGroupNode()) {\n <div class=\"tabs is-mb-1 | group-nodes-tabs\">\n <ul>\n @for (value of groupNodeValues(); track value) {\n <li [class.is-active]=\"selectedGroup() === value\">\n <a (click)=\"selectedGroup.set(value)\">\n <span class=\"is-capitalized is-pr-1\">{{ nodeKeyGroup() | pluralize: 1 }}:</span>\n <span>{{ groupNodeDisplayNames()[value] || value }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n}\n\n@if (isNodeKeyAllowed()) {\n @switch (selectedView()) {\n @case (View.table) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (hasData()) {\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (node of data(); track node.value.term.name) {\n @if (node.value.visible) {\n <th [attr.title]=\"node.value.term.name\">\n <he-node-link [node]=\"node.value.term\">\n <span\n [innerHtml]=\"node.value.term.name | ellipsis: 30 | compound: node.value.term.termType\"></span>\n </he-node-link>\n </th>\n }\n }\n </tr>\n <tr class=\"is-italic has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (node of data(); track node.value.term.name) {\n @if (node.value.visible) {\n <th [attr.title]=\"node.value.term.units\">\n <span [innerHtml]=\"node.value.term.units | compound\"></span>\n <he-terms-units-description class=\"is-inline-block is-ml-2\" [term]=\"node.value.term\" />\n </th>\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (site of sites(); track site['@id']; let siteIndex = $index) {\n <tr [class.is-suggested]=\"$any(site).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(site)\">\n <he-node-link [node]=\"site\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(site).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ siteIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(site) }}</span>\n </span>\n </he-node-link>\n </td>\n @for (node of data(); track node.value.term.name) {\n @if (node.value.visible) {\n @let siteData = node.value.values[site['@id']];\n <td class=\"is-nowrap\">\n @if (siteData) {\n <span\n class=\"trigger-popover\"\n [ngbPopover]=\"details\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\"\n [popoverContext]=\"{ data: siteData, site }\">\n <span pointer>\n {{ siteData.propertyValue | precision: 3 | default: '-' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"siteData.node\"\n key=\"value\" />\n </span>\n }\n @if (isMeasurement() && !siteData) {\n <span>\n <span>-</span>\n @if (siteTooBig(site)) {\n <sup class=\"pl-1\">(1)</sup>\n }\n </span>\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-justify-content-space-between is-mt-2\">\n <he-blank-node-state-notice [dataState]=\"dataState()\" />\n <div class=\"is-flex is-flex-wrap-wrap is-gap-8\">\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideZeroValues'\"\n name=\"hideZeroValues\"\n [(ngModel)]=\"hideZeroValues\" />\n <label [for]=\"componentId() + 'hideZeroValues'\">\n <span>\n Hide\n <b>0</b>\n values\n </span>\n </label>\n </div>\n @if (sites().length > 1) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideIdenticalValues'\"\n name=\"hideIdenticalValues\"\n [(ngModel)]=\"hideIdenticalValues\" />\n <label [for]=\"componentId() + 'hideIdenticalValues'\">\n <span>Hide identical values</span>\n </label>\n </div>\n }\n </div>\n </div>\n @if (showAreaTooBig()) {\n <p class=\"is-size-7 is-italic\">\n (1) This region is >{{ maxAreaSize }}km2 and is too large to reliably gap fill Measurements.\n </p>\n }\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No data available</span>\n @if (filterTerm()) {\n <span class=\"is-pl-1\">matching your search criteria</span>\n }\n <span>.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.chart) {\n @if (nodeKey() === BlankNodesKey.management) {\n @if (selectedNode()) {\n <he-sites-management-chart [site]=\"selectedNode()\" [cycles]=\"cycles()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-sites-management-chart>\n }\n }\n }\n @case (View.logs) {\n @if (selectedNode()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-node-logs-models\n [node]=\"selectedNode()\"\n [nodeKey]=\"nodeKey()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\"\n [filterTermTypes]=\"filterTermTypes()\"\n [filterTermTypesLabel]=\"nodeKey() | capitalize\" />\n }\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost is-p-2\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n\n <he-search-extend\n class=\"is-secondary\"\n collapsedClass=\"is-p-2\"\n placeholder=\"Filter terms by name\"\n (searchText)=\"filterTerm.set($event)\" />\n } @else if (showSelectSite()) {\n <ng-container *ngTemplateOutlet=\"selectSite\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectSite>\n @if (sites().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary\" for=\"selectSite\">Site</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectSite\">\n @for (value of sites(); track value; let siteIndex = $index) {\n <option [value]=\"siteIndex\">{{ siteIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #emptyValue>\n <span>-</span>\n</ng-template>\n\n<ng-template #details let-node=\"site\" let-data=\"data\">\n <p>\n <b>{{ node.name }}</b>\n </p>\n <he-node-value-details\n class=\"is-overflow-visible\"\n [data]=\"data\"\n [dataState]=\"dataState()\"\n [nodeType]=\"node['@type']\"\n [dataKey]=\"nodeKey()\"\n [aggregated]=\"node.aggregated\" />\n</ng-template>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: SearchExtendComponent, selector: "he-search-extend", inputs: ["value", "disabled", "placeholder", "class", "collapsedClass"], outputs: ["valueChange", "searchText"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: TermsUnitsDescriptionComponent, selector: "he-terms-units-description", inputs: ["term", "iconTemplate"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle"] }, { kind: "component", type: NodeValueDetailsComponent, selector: "he-node-value-details", inputs: ["data", "nodeType", "dataState", "dataKey", "aggregated"] }, { kind: "component", type: SitesManagementChartComponent, selector: "he-sites-management-chart", inputs: ["site", "cycles"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: EllipsisPipe, name: "ellipsis" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: CapitalizePipe, name: "capitalize" }, { kind: "pipe", type: PluralizePipe, name: "pluralize" }] }); }
16501
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SitesNodesComponent, isStandalone: true, selector: "he-sites-nodes", inputs: { dataState: { classPropertyName: "dataState", publicName: "dataState", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, enableChart: { classPropertyName: "enableChart", publicName: "enableChart", isSignal: true, isRequired: false, transformFunction: null }, nodeKeyGroup: { classPropertyName: "nodeKeyGroup", publicName: "nodeKeyGroup", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isGroupNode()) {\n <div class=\"tabs is-mb-1 | group-nodes-tabs\">\n <ul>\n @for (value of groupNodeValues(); track value) {\n <li [class.is-active]=\"selectedGroup() === value\">\n <a (click)=\"selectedGroup.set(value)\">\n <span class=\"is-capitalized is-pr-1\">{{ nodeKeyGroup() | pluralize: 1 }}:</span>\n <span>{{ groupNodeDisplayNames()[value] || value }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n}\n\n@if (isNodeKeyAllowed()) {\n @switch (selectedView()) {\n @case (View.table) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (hasData()) {\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (node of data(); track node.value.term.name) {\n @if (node.value.visible) {\n <th [attr.title]=\"node.value.term.name\">\n <he-node-link [node]=\"node.value.term\">\n <span\n [innerHtml]=\"node.value.term.name | ellipsis: 30 | compound: node.value.term.termType\"></span>\n </he-node-link>\n </th>\n }\n }\n </tr>\n <tr class=\"is-italic has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n @for (node of data(); track node.value.term.name) {\n @if (node.value.visible) {\n <th [attr.title]=\"node.value.term.units\">\n <span [innerHtml]=\"node.value.term.units | compound\"></span>\n <he-terms-units-description class=\"is-inline-block is-ml-2\" [term]=\"node.value.term\" />\n </th>\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (site of sites(); track site['@id']; let siteIndex = $index) {\n <tr [class.is-suggested]=\"$any(site).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(site)\">\n <he-node-link [node]=\"site\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(site).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ siteIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(site) }}</span>\n </span>\n </he-node-link>\n </td>\n @for (node of data(); track node.value.term.name) {\n @if (node.value.visible) {\n @let siteData = node.value.values[site['@id']];\n <td class=\"is-nowrap\">\n @if (siteData) {\n <span\n class=\"trigger-popover\"\n [ngbPopover]=\"details\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\"\n [popoverContext]=\"{ data: siteData, site }\">\n <span pointer>\n {{ siteData.propertyValue | precision: 3 | default: '-' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"siteData.node\"\n key=\"value\" />\n </span>\n }\n @if (isMeasurement() && !siteData) {\n <span>\n <span>-</span>\n @if (siteTooBig(site)) {\n <sup class=\"pl-1\">(1)</sup>\n }\n </span>\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-justify-content-space-between is-mt-2\">\n <he-blank-node-state-notice [dataState]=\"dataState()\" />\n <div class=\"is-flex is-flex-wrap-wrap is-gap-8\">\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideZeroValues'\"\n name=\"hideZeroValues\"\n [(ngModel)]=\"hideZeroValues\" />\n <label [for]=\"componentId() + 'hideZeroValues'\">\n <span>\n Hide\n <b>0</b>\n values\n </span>\n </label>\n </div>\n @if (sites().length > 1) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideIdenticalValues'\"\n name=\"hideIdenticalValues\"\n [(ngModel)]=\"hideIdenticalValues\" />\n <label [for]=\"componentId() + 'hideIdenticalValues'\">\n <span>Hide identical values</span>\n </label>\n </div>\n }\n </div>\n </div>\n @if (showAreaTooBig()) {\n <p class=\"is-size-7 is-italic\">\n (1) This region is >{{ maxAreaSize }}km2 and is too large to reliably gap fill Measurements.\n </p>\n }\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No data available</span>\n @if (filterTerm()) {\n <span class=\"is-pl-1\">matching your search criteria</span>\n }\n <span>.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.chart) {\n @if (nodeKey() === BlankNodesKey.management) {\n @if (selectedNode()) {\n <he-sites-management-chart [site]=\"selectedNode()\" [cycles]=\"cycles()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-sites-management-chart>\n }\n }\n }\n @case (View.logs) {\n @if (selectedNode()) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n <he-node-logs-models\n [node]=\"selectedNode()\"\n [nodeKey]=\"nodeKey()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\"\n [filterTermTypes]=\"filterTermTypes()\"\n [filterTermTypesLabel]=\"nodeKey() | capitalize\" />\n }\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost is-p-2\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n\n <he-search-extend\n class=\"is-secondary\"\n collapsedClass=\"is-p-2\"\n placeholder=\"Filter terms by name\"\n (searchText)=\"filterTerm.set($event)\" />\n } @else if (showSelectSite()) {\n <ng-container *ngTemplateOutlet=\"selectSite\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectSite>\n @if (sites().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary\" for=\"selectSite\">Site</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectIndex($event)\" if=\"selectSite\">\n @for (value of sites(); track value; let siteIndex = $index) {\n <option [value]=\"siteIndex\">{{ siteIndex + 1 }}. {{ defaultLabel(value) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #emptyValue>\n <span>-</span>\n</ng-template>\n\n<ng-template #details let-node=\"site\" let-data=\"data\">\n <p>\n <b>{{ node.name }}</b>\n </p>\n <he-node-value-details\n class=\"is-overflow-visible\"\n [data]=\"data\"\n [dataState]=\"dataState()\"\n [nodeType]=\"node['@type']\"\n [dataKey]=\"nodeKey()\"\n [aggregated]=\"node.aggregated\" />\n</ng-template>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: SearchExtendComponent, selector: "he-search-extend", inputs: ["value", "disabled", "placeholder", "class", "collapsedClass"], outputs: ["valueChange", "searchText"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: TermsUnitsDescriptionComponent, selector: "he-terms-units-description", inputs: ["term", "iconTemplate"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["dataState", "nodeType", "dataKey", "key", "node", "state", "linkClass"] }, { kind: "component", type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["node", "nodeKey", "originalValues", "recalculatedValues", "terms", "filterTermTypes", "filterTermTypesLabel", "logsKey", "noDataMessage", "cycle", "jlogParentKey", "jlogParentIndex"] }, { kind: "component", type: NodeValueDetailsComponent, selector: "he-node-value-details", inputs: ["data", "nodeType", "dataState", "dataKey", "aggregated"] }, { kind: "component", type: SitesManagementChartComponent, selector: "he-sites-management-chart", inputs: ["site", "cycles"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: EllipsisPipe, name: "ellipsis" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: CapitalizePipe, name: "capitalize" }, { kind: "pipe", type: PluralizePipe, name: "pluralize" }] }); }
15563
16502
  }
15564
16503
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SitesNodesComponent, decorators: [{
15565
16504
  type: Component$1,
@@ -15646,5 +16585,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
15646
16585
  * Generated bundle index. Do not edit.
15647
16586
  */
15648
16587
 
15649
- export { ARRAY_DELIMITER, ApplyPurePipe, BarChartComponent, BibliographiesSearchConfirmComponent, BlankNodeStateComponent, BlankNodeStateNoticeComponent, BlankNodeValueDeltaComponent, CapitalizePipe, ChartComponent, ChartConfigurationDirective, ChartExportButtonComponent, ChartTooltipComponent, ClickOutsideDirective, ClipboardComponent, CollapsibleBoxComponent, CollapsibleBoxStyle, ColorPalette, CompoundDirective, CompoundPipe, ContributionChartComponent, ControlValueAccessor, CycleNodesKeyGroup, CyclesCompletenessComponent, CyclesEmissionsCategoryService, CyclesEmissionsChartComponent, CyclesFunctionalUnitMeasureComponent, CyclesMetadataComponent, CyclesNodesComponent, CyclesNodesTimelineComponent, CyclesResultComponent, DataTableComponent, DefaultPipe, DeltaColour, DistributionChartComponent, DrawerContainerComponent, DurationPipe, EllipsisPipe, EngineModelsLinkComponent, EngineModelsLookupInfoComponent, EngineModelsStageComponent, EngineModelsStageDeepComponent, EngineModelsStageDeepService, EngineModelsVersionInfoComponent, EngineModelsVersionLinkComponent, EngineOrchestratorEditComponent, EngineRequirementsFormComponent, FileSizePipe, FileUploadErrorKeys, FilesErrorSummaryComponent, FilesFormComponent, FilesFormEditableComponent, FilesUploadErrorsComponent, FilterAccordionComponent, GUIDE_ENABLED, GetPipe, GlossaryMigrationFormat, GuideOverlayComponent, HESvgIconComponent, HE_API_BASE_URL, HE_CALCULATIONS_BASE_URL, HE_MAP_LOADED, HeAuthService, HeCommonService, HeEngineService, HeGlossaryService, HeMendeleyService, HeNodeCsvService, HeNodeService, HeNodeStoreService, HeSchemaService, HeSearchService, HeToastService, HorizontalBarChartComponent, HorizontalButtonsGroupComponent, ImpactAssessmentsGraphComponent, ImpactAssessmentsIndicatorBreakdownChartComponent, ImpactAssessmentsIndicatorsChartComponent, ImpactAssessmentsProductsComponent, IsArrayPipe, IsObjectPipe, IssueConfirmComponent, KeyToLabelPipe, Level, LineChartComponent, LinkKeyValueComponent, LogStatus, LongPressDirective, MAX_RESULTS, MapsDrawingComponent, MapsDrawingConfirmComponent, MaxPipe, MeanPipe, MedianPipe, MendeleySearchResult, MinPipe, MobileShellComponent, NavigationMenuComponent, NoExtPipe, NodeAggregatedComponent, NodeAggregatedInfoComponent, NodeAggregatedQualityScoreComponent, NodeCsvExportConfirmComponent, NodeCsvPreviewComponent, NodeCsvSelectHeadersComponent, NodeIconComponent, NodeJsonldComponent, NodeJsonldSchemaComponent, NodeKeyState, NodeLinkComponent, NodeLogsFileComponent, NodeLogsModelsComponent, NodeLogsTimeComponent, NodeMissingLookupFactorsComponent, NodeQualityScore, NodeRecommendationsComponent, NodeSelectComponent, NodeValueDetailsComponent, PipelineStagesProgressComponent, PluralizePipe, PopoverComponent, PopoverConfirmComponent, PrecisionPipe, RelatedNodeResult, RemoveMarkdownPipe, RepeatPipe, Repository, ResizedDirective, ResizedEvent, ResponsiveService, SchemaInfoComponent, SchemaVersionLinkComponent, SearchExtendComponent, ShelfDialogComponent, ShellComponent, SiteNodesKeyGroup, SitesManagementChartComponent, SitesMapsComponent, SitesNodesComponent, SkeletonTextComponent, SocialTagsComponent, SortByPipe, SortSelectComponent, SumPipe, TagsInputDirective, Template, TermsPropertyContentComponent, TermsSubClassOfContentComponent, TermsUnitsDescriptionComponent, ThousandSuffixesPipe, ThousandsPipe, TimesPipe, ToastComponent, UncapitalizePipe, addPolygonToFeature, afterBarDrawPlugin, allCountriesQuery, allGroups, allOptions, availableProperties, axisHoverPlugin, backgroundHoverPlugin, baseApiUrl, baseUrl, bottom, buildSummary, bytesSize, calculateCycleDuration, calculateCycleDurationEnabled, calculateCycleStartDate, calculateCycleStartDateEnabled, capitalize, changelogUrl, clustererImage, code, colorToRgba, compoundToHtml, computeKeys, computeTerms, contactUsEmail, contactUsLink, convertToSvg, coordinatesToPoint, copyObject, countGroupVisibleNodes, countriesQuery, createMarker, cropsQuery, d3ellipse, d3wrap, dataPathLabel, dataPathToKey, dataVersionHeader, dataVersionHeaderKey, defaultFeature, defaultLabel, defaultSuggestionType, defaultSvgIconSize, defaultTicksFont, definitionToSchemaType, distinctUntilChangedDeep, downloadFile, downloadPng, downloadSvg, ellipsis, engineGitBaseUrl, engineGitUrl, errorHasError, errorHasWarning, errorText, evaluateSuccess, exportAsSVG, exportFormats, externalLink, externalNodeLink, fillColor, fillStyle, filterBlankNode$1 as filterBlankNode, filterError, filterParams, findConfigModels, findMatchingModel, findModels, findNodeModel, findOrchestratorModel, findProperty, findPropertyById, flatFilterData, flatFilterNode, formatCustomErrorMessage, formatDate, formatError, formatPropertyError, formatter, getColor, getDatesBetween, gitBranch, gitHome, gitlabRawUrl, glossaryBaseUrl, glossaryLink, groupChanged, groupLogsByTerm, groupNodesByTerm, groupdLogsByKey, grouppedKeys, grouppedValueKeys, groupsLogsByFields, guideModelUrl, guideNamespace, guidePageId, handleAPIError, handleGuideEvent, hasError, hasValidationError, hasWarning, hexToRgba, iconSizes, icons, ignoreKeys$2 as ignoreKeys, increaseScaleLimits, initialFilterState, injectResizeEvent$, inputGroupsTermTypes, isAddPropertyEnabled, isChrome, isDateBetween, isEqual, isExternal, isKeyClosedVisible, isKeyHidden, isMaxStage, isMethodModelAllowed, isMigrationError, isMissingOneOfError, isMissingPropertyError, isNonNodeModelKey, isSchemaIri, isScrolledBelow, isState, isTermTypeAllowed, isValidKey, keyToDataPath, levels, listColor, listColorContinuous, listColorWithAlpha, loadMapApi, loadSvgSprite, locationQuery, logToCsv$2 as logToCsv, logValueArray, logsKey, lollipopChartPlugin, lookupUrl, mapFilterData, mapsUrl, markerIcon, markerPie, matchAggregatedQuery, matchAggregatedValidatedQuery, matchBoolPrefixQuery, matchCountry, matchExactQuery, matchGlobalRegion, matchId, matchNameNormalized, matchNestedKey, matchPhrasePrefixQuery, matchPhraseQuery, matchPrimaryProductQuery, matchQuery, matchRegex, matchRegion, matchTermType, matchType, maxAreaSize, measurementValue, mergeDataWithHeaders, methodTierOrder, migrationErrorMessage, migrationsUrl, missingNodeErrors, modelCount, modelKeyParams, modelParams, models, multiMatchQuery, nestedProperty, nestingEnabled, nestingTypeEnabled, noValue, nodeAvailableProperties, nodeById, nodeColours$1 as nodeColours, nodeDataState, nodeDataStates, nodeId, nodeIds, nodeLink, nodeLinkEnabled, nodeLinkTypeEnabled, nodeLogsUrl, nodeQualityScoreColor, nodeQualityScoreLevel, nodeQualityScoreMaxDefault, nodeQualityScoreOrder, nodeSecondaryColours, nodeToAggregationFilename, nodeType, nodeTypeDataState, nodeTypeIcon, nodeTypeIconSchema, nodeUrl, nodeUrlParams, nodeVersion, nodesByState, nodesByType, numberGte, optionsFromGroup, parentKey, parentProperty, parseColor, parseData, parseDataPath, parseLines, parseMessage, parseNewValue, pluralize, pointToCoordinates, polygonBounds, polygonToCoordinates, polygonToMap, polygonsFromFeature, populateWithTrackIdsFilterData, postGuideEvent, primaryProduct, productsQuery, propertyError, propertyId, recursiveProperties, refToSchemaType, refreshPropertyKeys, regionsQuery, registerChart, repeat, reportIssueLink, reportIssueUrl, safeJSONParse, safeJSONStringify, schemaBaseUrl, schemaDataBaseUrl, schemaLink, schemaRequiredProperties, schemaTypeToDefaultValue, scrollToEl, scrollTop, searchFilterData, searchableTypes, siblingProperty, simplifyContributions, singleProperty, siteTooBig, siteTypeToColor, siteTypeToIcon, sortProperties, sortedDates, strokeColor, strokeStyle, subValueKeys, suggestMatchQuery, suggestQuery, takeAfterViewInit, termLocation, termLocationName, termProperties, termTypeLabel, toSnakeCase, toThousands, typeToNewProperty, typeaheadFocus, uncapitalize, uniqueDatesBetween, updateProperties, valueLink, valueToString, valueTypeToDefault, valueValue, waitFor, wildcardQuery };
16588
+ export { ARRAY_DELIMITER, ApplyPurePipe, BarChartComponent, BibliographiesSearchConfirmComponent, BlankNodeStateComponent, BlankNodeStateNoticeComponent, BlankNodeValueDeltaComponent, CapitalizePipe, ChartComponent, ChartConfigurationDirective, ChartExportButtonComponent, ChartTooltipComponent, ClickOutsideDirective, ClipboardComponent, CollapsibleBoxComponent, CollapsibleBoxStyle, ColorPalette, CompoundDirective, CompoundPipe, ContributionChartComponent, ControlValueAccessor, CycleNodesKeyGroup, CyclesCompletenessComponent, CyclesEmissionsCategoryService, CyclesEmissionsChartComponent, CyclesFunctionalUnitMeasureComponent, CyclesMetadataComponent, CyclesNodesComponent, CyclesNodesTimelineComponent, CyclesResultComponent, DataTableComponent, DefaultPipe, DeltaColour, DistributionChartComponent, DrawerContainerComponent, DurationPipe, EllipsisPipe, EngineModelsLinkComponent, EngineModelsLookupInfoComponent, EngineModelsStageComponent, EngineModelsStageDeepComponent, EngineModelsStageDeepService, EngineModelsVersionInfoComponent, EngineModelsVersionLinkComponent, EngineOrchestratorEditComponent, EngineRequirementsFormComponent, FileSizePipe, FileUploadErrorKeys, FilesErrorSummaryComponent, FilesFormComponent, FilesFormEditableComponent, FilesUploadErrorsComponent, FilterAccordionComponent, GUIDE_ENABLED, GetPipe, GlossaryMigrationFormat, GuideOverlayComponent, HESvgIconComponent, HE_API_BASE_URL, HE_CALCULATIONS_BASE_URL, HE_MAP_LOADED, HeAuthService, HeCommonService, HeEngineService, HeGlossaryService, HeMendeleyService, HeNodeCsvService, HeNodeService, HeNodeStoreService, HeSchemaService, HeSearchService, HeToastService, HorizontalBarChartComponent, HorizontalButtonsGroupComponent, ImpactAssessmentsGraphComponent, ImpactAssessmentsIndicatorBreakdownChartComponent, ImpactAssessmentsIndicatorsChartComponent, ImpactAssessmentsProductsComponent, IsArrayPipe, IsObjectPipe, IssueConfirmComponent, KeyToLabelPipe, Level, LineChartComponent, LinkKeyValueComponent, LogStatus, LongPressDirective, MAX_RESULTS, MapsDrawingComponent, MapsDrawingConfirmComponent, MaxPipe, MeanPipe, MedianPipe, MendeleySearchResult, MinPipe, MobileShellComponent, NavigationMenuComponent, NoExtPipe, NodeAggregatedComponent, NodeAggregatedInfoComponent, NodeAggregatedQualityScoreComponent, NodeCsvExportConfirmComponent, NodeCsvPreviewComponent, NodeCsvSelectHeadersComponent, NodeIconComponent, NodeJLogModelsComponent, NodeJsonldComponent, NodeJsonldSchemaComponent, NodeKeyState, NodeLinkComponent, NodeLogsFileComponent, NodeLogsModelsComponent, NodeLogsTimeComponent, NodeMissingLookupFactorsComponent, NodeQualityScore, NodeRecommendationsComponent, NodeSelectComponent, NodeValueDetailsComponent, PipelineStagesProgressComponent, PluralizePipe, PopoverComponent, PopoverConfirmComponent, PrecisionPipe, RelatedNodeResult, RemoveMarkdownPipe, RepeatPipe, Repository, ResizedDirective, ResizedEvent, ResponsiveService, SchemaInfoComponent, SchemaVersionLinkComponent, SearchExtendComponent, ShelfDialogComponent, ShellComponent, SiteNodesKeyGroup, SitesManagementChartComponent, SitesMapsComponent, SitesNodesComponent, SkeletonTextComponent, SocialTagsComponent, SortByPipe, SortSelectComponent, SumPipe, TagsInputDirective, Template, TermsPropertyContentComponent, TermsSubClassOfContentComponent, TermsUnitsDescriptionComponent, ThousandSuffixesPipe, ThousandsPipe, TimesPipe, ToastComponent, UncapitalizePipe, addPolygonToFeature, afterBarDrawPlugin, allCountriesQuery, allGroups, allOptions, availableProperties, axisHoverPlugin, backgroundHoverPlugin, baseApiUrl, baseUrl, bottom, buildSummary, bytesSize, calculateCycleDuration, calculateCycleDurationEnabled, calculateCycleStartDate, calculateCycleStartDateEnabled, capitalize, changelogUrl, clustererImage, code, colorToRgba, compoundToHtml, computeKeys, computeTerms, contactUsEmail, contactUsLink, convertToSvg, coordinatesToPoint, copyObject, countGroupVisibleNodes, countriesQuery, createMarker, cropsQuery, d3ellipse, d3wrap, dataPathLabel, dataPathToKey, dataVersionHeader, dataVersionHeaderKey, defaultFeature, defaultLabel, defaultSuggestionType, defaultSvgIconSize, defaultTicksFont, definitionToSchemaType, distinctUntilChangedDeep, downloadFile, downloadPng, downloadSvg, ellipsis, engineGitBaseUrl, engineGitUrl, errorHasError, errorHasWarning, errorText, evaluateSuccess, exportAsSVG, exportFormats, externalLink, externalNodeLink, fillColor, fillStyle, filterBlankNode$1 as filterBlankNode, filterError, filterParams, findConfigModels, findMatchingModel, findModels, findNodeModel, findOrchestratorModel, findProperty, findPropertyById, flatFilterData, flatFilterNode, formatCustomErrorMessage, formatDate, formatError, formatPropertyError, formatter, getColor, getDatesBetween, gitBranch, gitHome, gitlabRawUrl, glossaryBaseUrl, glossaryLink, groupChanged, groupJLogByField, groupJLogByTerm, groupLogsByTerm, groupNodesByTerm, groupdLogsByKey, grouppedKeys, grouppedValueKeys, groupsLogsByFields, guideModelUrl, guideNamespace, guidePageId, handleAPIError, handleGuideEvent, hasError, hasValidationError, hasWarning, hexToRgba, iconSizes, icons, ignoreKeys$2 as ignoreKeys, increaseScaleLimits, initialFilterState, injectResizeEvent$, inputGroupsTermTypes, isAddPropertyEnabled, isChrome, isDateBetween, isEqual, isExternal, isKeyClosedVisible, isKeyHidden, isMaxStage, isMethodModelAllowed, isMigrationError, isMissingOneOfError, isMissingPropertyError, isNonNodeModelKey, isSchemaIri, isScrolledBelow, isState, isTermTypeAllowed, isValidKey, jLogModelCount, keyToDataPath, levels, listColor, listColorContinuous, listColorWithAlpha, loadMapApi, loadSvgSprite, locationQuery, logToCsv$2 as logToCsv, logValueArray, logsKey, lollipopChartPlugin, lookupUrl, mapFilterData, mapsUrl, markerIcon, markerPie, matchAggregatedQuery, matchAggregatedValidatedQuery, matchBoolPrefixQuery, matchCountry, matchExactQuery, matchGlobalRegion, matchId, matchNameNormalized, matchNestedKey, matchPhrasePrefixQuery, matchPhraseQuery, matchPrimaryProductQuery, matchQuery, matchRegex, matchRegion, matchTermType, matchType, maxAreaSize, measurementValue, mergeDataWithHeaders, methodTierOrder, migrationErrorMessage, migrationsUrl, missingNodeErrors, modelCount, modelKeyParams, modelParams, models, multiMatchQuery, nestedProperty, nestingEnabled, nestingTypeEnabled, noValue, nodeAvailableProperties, nodeById, nodeColours$1 as nodeColours, nodeDataState, nodeDataStates, nodeId, nodeIds, nodeLink, nodeLinkEnabled, nodeLinkTypeEnabled, nodeLogsUrl, nodeQualityScoreColor, nodeQualityScoreLevel, nodeQualityScoreMaxDefault, nodeQualityScoreOrder, nodeSecondaryColours, nodeToAggregationFilename, nodeType, nodeTypeDataState, nodeTypeIcon, nodeTypeIconSchema, nodeUrl, nodeUrlParams, nodeVersion, nodesByState, nodesByType, numberGte, optionsFromGroup, parentKey, parentProperty, parseColor, parseData, parseDataPath, parseLines, parseMessage, parseNewValue, pluralize, pointToCoordinates, polygonBounds, polygonToCoordinates, polygonToMap, polygonsFromFeature, populateWithTrackIdsFilterData, postGuideEvent, primaryProduct, productsQuery, propertyError, propertyId, recursiveProperties, refToSchemaType, refreshPropertyKeys, regionsQuery, registerChart, repeat, reportIssueLink, reportIssueUrl, safeJSONParse, safeJSONStringify, schemaBaseUrl, schemaDataBaseUrl, schemaLink, schemaRequiredProperties, schemaTypeToDefaultValue, scrollToEl, scrollTop, searchFilterData, searchableTypes, siblingProperty, simplifyContributions, singleProperty, siteTooBig, siteTypeToColor, siteTypeToIcon, sortProperties, sortedDates, strokeColor, strokeStyle, subValueKeys, suggestMatchQuery, suggestQuery, takeAfterViewInit, termLocation, termLocationName, termProperties, termTypeLabel, toSnakeCase, toThousands, typeToNewProperty, typeaheadFocus, uncapitalize, uniqueDatesBetween, updateProperties, valueLink, valueToString, valueTypeToDefault, valueValue, waitFor, wildcardQuery };
15650
16589
  //# sourceMappingURL=hestia-earth-ui-components.mjs.map