@eric-emg/symphiq-components 1.2.194 → 1.2.196

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.
@@ -1,4 +1,4 @@
1
- import { ViewModeEnum, MetricStatusEnum, TrendDirectionEnum, CompetitiveScoreEnum, MetricEnum, DimensionEnum, UiDataPeriodEnum, UiDataComparePeriodEnum, ChartTypeEnum, IconSourceEnum, ProfileAnalysisRecommendationPriorityEnum, ProfileItemTypeEnum, PriceVsCompetitorsEnum, DifferentiationStrengthEnum, ThreatLevelEnum, normalizeToV3, FocusAreaDetailStatusEnum, FocusAreaDomainEnumUtil, FocusAreaDomainEnum, ShopDataLoadStatusEnum, AiDynamicContentStatusEnum, FocusAreaHealthEnum, ProfileAnalysisPriorityEnum, CapabilityStateEnum, QuadrantEnum, AdvantageEnum, OverallGradeEnum, OperationalMaturityEnum, ProfileAnalysisEffortLevelEnum, ProfileAnalysisImpactLevelEnum, ProfileAnalysisTypeEnum, LineChartUseCaseEnum, BarChartUseCaseEnum } from '@jebgem/model';
1
+ import { ViewModeEnum, MetricStatusEnum, TrendDirectionEnum, CompetitiveScoreEnum, MetricEnum, DimensionEnum, UiDataPeriodEnum, UiDataComparePeriodEnum, ChartTypeEnum, IconSourceEnum, ProfileAnalysisRecommendationPriorityEnum, ProfileItemTypeEnum, PriceVsCompetitorsEnum, DifferentiationStrengthEnum, ThreatLevelEnum, normalizeToV3, FocusAreaDetailStatusEnum, FocusAreaDomainEnumUtil, FocusAreaDomainEnum, ShopDataLoadStatusEnum, MetricEnumUtil, NumberTypeEnum, AiDynamicContentStatusEnum, FocusAreaHealthEnum, ProfileAnalysisPriorityEnum, CapabilityStateEnum, QuadrantEnum, AdvantageEnum, OverallGradeEnum, OperationalMaturityEnum, ProfileAnalysisEffortLevelEnum, ProfileAnalysisImpactLevelEnum, ProfileAnalysisTypeEnum, LineChartUseCaseEnum, BarChartUseCaseEnum } from '@jebgem/model';
2
2
  export * from '@jebgem/model';
3
3
  import * as i0 from '@angular/core';
4
4
  import { Injectable, signal, computed, input, ChangeDetectionStrategy, Component, output, inject, ElementRef, Renderer2, effect, Directive, HostListener, untracked, ViewChild, PLATFORM_ID, Inject, Input } from '@angular/core';
@@ -2920,9 +2920,18 @@ function calculateRelatedMetricRatios(funnelMetrics, baselineValues) {
2920
2920
  return ratios;
2921
2921
  }
2922
2922
  function calculateMetricTargetsFromRevenueReverse(revenueTarget, priorYearRevenue, funnelMetrics, baselineValues) {
2923
+ console.log('\nšŸ”„ === REVERSE REVENUE CALCULATOR START ===');
2924
+ console.log('šŸ“Š Inputs:');
2925
+ console.log(' - Revenue Target:', revenueTarget);
2926
+ console.log(' - Prior Year Revenue:', priorYearRevenue);
2927
+ console.log(' - Funnel Metrics Count:', funnelMetrics.length);
2928
+ console.log(' - Baseline Values:', Array.from(baselineValues.entries()));
2923
2929
  const revenuePercentageIncrease = ((revenueTarget - priorYearRevenue) / priorYearRevenue) * 100;
2930
+ console.log(' - Revenue % Increase:', revenuePercentageIncrease.toFixed(2) + '%');
2924
2931
  const funnelRatios = calculateFunnelRatios(funnelMetrics, baselineValues);
2932
+ console.log('\nšŸ“ˆ Funnel Ratios:', Array.from(funnelRatios.entries()));
2925
2933
  const relatedRatios = calculateRelatedMetricRatios(funnelMetrics, baselineValues);
2934
+ console.log('šŸ“ˆ Related Metric Ratios:', Array.from(relatedRatios.entries()));
2926
2935
  const sortedFunnelMetrics = [...funnelMetrics].sort((a, b) => {
2927
2936
  const aFunnel = a.funnelInd ?? 999;
2928
2937
  const bFunnel = b.funnelInd ?? 999;
@@ -2937,12 +2946,14 @@ function calculateMetricTargetsFromRevenueReverse(revenueTarget, priorYearRevenu
2937
2946
  .map(fm => fm.relatedMetric)
2938
2947
  .filter(Boolean);
2939
2948
  const stageTargets = new Map();
2949
+ console.log('\nšŸŽÆ Calculating Stage Targets (Reverse Order):');
2940
2950
  let currentRevenue = revenueTarget;
2941
2951
  for (let i = stages.length - 1; i >= 0; i--) {
2942
2952
  const stage = stages[i];
2943
2953
  const baseline = baselineValues.get(stage) || 0;
2944
2954
  if (i === stages.length - 1) {
2945
2955
  stageTargets.set(stage, currentRevenue);
2956
+ console.log(` Stage ${i} (${stage}): ${currentRevenue.toFixed(2)} (Revenue Stage)`);
2946
2957
  }
2947
2958
  else {
2948
2959
  const nextStage = stages[i + 1];
@@ -2952,13 +2963,17 @@ function calculateMetricTargetsFromRevenueReverse(revenueTarget, priorYearRevenu
2952
2963
  const nextStageTarget = stageTargets.get(nextStage) || 0;
2953
2964
  currentRevenue = nextStageTarget / ratio;
2954
2965
  stageTargets.set(stage, currentRevenue);
2966
+ console.log(` Stage ${i} (${stage}): ${currentRevenue.toFixed(2)} (calculated from ${nextStage} with ratio ${ratio.toFixed(4)})`);
2955
2967
  }
2956
2968
  else {
2957
2969
  const increaseNeeded = revenueTarget / baseline;
2958
- stageTargets.set(stage, baseline * increaseNeeded);
2970
+ const calculatedValue = baseline * increaseNeeded;
2971
+ stageTargets.set(stage, calculatedValue);
2972
+ console.log(` Stage ${i} (${stage}): ${calculatedValue.toFixed(2)} (fallback: baseline * ${increaseNeeded.toFixed(4)})`);
2959
2973
  }
2960
2974
  }
2961
2975
  }
2976
+ console.log('šŸŽÆ Final Stage Targets:', Array.from(stageTargets.entries()));
2962
2977
  const metricCalculations = [];
2963
2978
  const stageGroups = new Map();
2964
2979
  sortedFunnelMetrics.forEach(fm => {
@@ -2969,30 +2984,49 @@ function calculateMetricTargetsFromRevenueReverse(revenueTarget, priorYearRevenu
2969
2984
  stageGroups.get(fm.funnelMetric).push(fm);
2970
2985
  }
2971
2986
  });
2987
+ console.log('\nšŸ“Š Calculating Individual Metric Targets:');
2972
2988
  stageGroups.forEach((metrics, funnelStage) => {
2973
2989
  const stageTarget = stageTargets.get(funnelStage);
2974
2990
  const stageBaseline = baselineValues.get(funnelStage) || 0;
2975
2991
  const stageIncrease = stageTarget ? stageTarget - stageBaseline : 0;
2992
+ console.log(`\n šŸ“ Funnel Stage: ${funnelStage}`);
2993
+ console.log(` - Stage Target: ${stageTarget?.toFixed(2)}`);
2994
+ console.log(` - Stage Baseline: ${stageBaseline.toFixed(2)}`);
2995
+ console.log(` - Stage Increase: ${stageIncrease.toFixed(2)}`);
2976
2996
  metrics.forEach(fm => {
2977
2997
  if (!fm.relatedMetric)
2978
2998
  return;
2979
2999
  const currentValue = baselineValues.get(fm.relatedMetric) || 0;
2980
3000
  const isFunnelStage = fm.funnelMetric === fm.relatedMetric;
3001
+ console.log(`\n šŸ”¹ Metric: ${fm.relatedMetric}${isFunnelStage ? ' (STAGE)' : ''}`);
3002
+ console.log(` Current Value: ${currentValue.toFixed(2)}`);
2981
3003
  let targetValue;
2982
3004
  let percentageIncrease;
2983
3005
  if (isFunnelStage && stageTarget !== undefined) {
2984
3006
  targetValue = stageTarget;
2985
3007
  percentageIncrease = currentValue > 0 ? ((targetValue - currentValue) / currentValue) * 100 : 0;
3008
+ console.log(` Method: Direct Stage Target`);
2986
3009
  }
2987
3010
  else if (fm.relatedMetric === MetricEnum.BOUNCE_RATE) {
2988
3011
  const stageTargetValue = stageTargets.get(funnelStage) || stageBaseline;
2989
3012
  const stageIncreaseRatio = stageBaseline > 0 ? stageTargetValue / stageBaseline : 1;
2990
3013
  targetValue = currentValue / stageIncreaseRatio;
2991
3014
  percentageIncrease = currentValue > 0 ? ((targetValue - currentValue) / currentValue) * 100 : 0;
3015
+ console.log(` Method: Bounce Rate (inverse)`);
3016
+ console.log(` Stage Increase Ratio: ${stageIncreaseRatio.toFixed(4)}`);
2992
3017
  }
2993
3018
  else if (isDerivedMetric(fm.relatedMetric)) {
2994
3019
  targetValue = currentValue;
2995
3020
  percentageIncrease = 0;
3021
+ console.log(` Method: Derived Metric (no change)`);
3022
+ }
3023
+ else if (isRateMetric(fm.relatedMetric)) {
3024
+ const stageTargetValue = stageTargets.get(funnelStage) || stageBaseline;
3025
+ const stageIncreaseRatio = stageBaseline > 0 ? stageTargetValue / stageBaseline : 1;
3026
+ targetValue = currentValue * stageIncreaseRatio;
3027
+ percentageIncrease = currentValue > 0 ? ((targetValue - currentValue) / currentValue) * 100 : 0;
3028
+ console.log(` Method: Rate Metric (proportional scaling)`);
3029
+ console.log(` Stage Increase Ratio: ${stageIncreaseRatio.toFixed(4)}`);
2996
3030
  }
2997
3031
  else {
2998
3032
  const ratioKey = `${fm.relatedMetric}_to_${funnelStage}`;
@@ -3000,6 +3034,7 @@ function calculateMetricTargetsFromRevenueReverse(revenueTarget, priorYearRevenu
3000
3034
  const relatedMetrics = metrics.filter(m => m.relatedMetric &&
3001
3035
  m.relatedMetric !== funnelStage &&
3002
3036
  !isDerivedMetric(m.relatedMetric) &&
3037
+ !isRateMetric(m.relatedMetric) &&
3003
3038
  m.relatedMetric !== MetricEnum.BOUNCE_RATE);
3004
3039
  const numRelatedMetrics = relatedMetrics.length;
3005
3040
  if (numRelatedMetrics > 0 && stageBaseline > 0) {
@@ -3007,14 +3042,23 @@ function calculateMetricTargetsFromRevenueReverse(revenueTarget, priorYearRevenu
3007
3042
  const metricIncreaseNeeded = impactRatio > 0 ? avgIncreaseNeeded / impactRatio : avgIncreaseNeeded;
3008
3043
  targetValue = currentValue + metricIncreaseNeeded;
3009
3044
  percentageIncrease = currentValue > 0 ? ((targetValue - currentValue) / currentValue) * 100 : 0;
3045
+ console.log(` Method: Related Metric with Impact Ratio`);
3046
+ console.log(` Num Related Metrics: ${numRelatedMetrics}`);
3047
+ console.log(` Avg Increase Needed: ${avgIncreaseNeeded.toFixed(2)}`);
3048
+ console.log(` Impact Ratio: ${impactRatio.toFixed(4)}`);
3049
+ console.log(` Metric Increase Needed: ${metricIncreaseNeeded.toFixed(2)}`);
3010
3050
  }
3011
3051
  else {
3012
3052
  const stageTargetValue = stageTargets.get(funnelStage) || stageBaseline;
3013
3053
  const stageIncreaseRatio = stageBaseline > 0 ? stageTargetValue / stageBaseline : 1;
3014
3054
  targetValue = currentValue * stageIncreaseRatio;
3015
3055
  percentageIncrease = currentValue > 0 ? ((targetValue - currentValue) / currentValue) * 100 : 0;
3056
+ console.log(` Method: Proportional to Stage`);
3057
+ console.log(` Stage Increase Ratio: ${stageIncreaseRatio.toFixed(4)}`);
3016
3058
  }
3017
3059
  }
3060
+ console.log(` āœ… Target Value: ${targetValue.toFixed(2)}`);
3061
+ console.log(` āœ… % Increase: ${percentageIncrease.toFixed(2)}%`);
3018
3062
  metricCalculations.push({
3019
3063
  metric: fm.relatedMetric,
3020
3064
  funnelMetric: fm.funnelMetric,
@@ -3028,19 +3072,34 @@ function calculateMetricTargetsFromRevenueReverse(revenueTarget, priorYearRevenu
3028
3072
  });
3029
3073
  });
3030
3074
  });
3075
+ console.log('\nāœ… Total Metric Calculations:', metricCalculations.length);
3031
3076
  const validation = validateRevenueTarget(revenueTarget, metricCalculations, baselineValues, funnelRatios);
3077
+ console.log('\nšŸ” Validation Results:');
3078
+ console.log(' - Target Revenue:', revenueTarget.toFixed(2));
3079
+ console.log(' - Calculated Revenue:', validation.calculatedRevenue.toFixed(2));
3080
+ console.log(' - Difference:', validation.difference.toFixed(2));
3081
+ console.log(' - % Difference:', validation.percentageDifference.toFixed(4) + '%');
3082
+ console.log(' - Within Tolerance:', validation.withinTolerance);
3032
3083
  let adjustmentApplied = 0;
3033
3084
  if (Math.abs(validation.difference) > 0.01) {
3085
+ console.log('\nāš™ļø Applying Precision Adjustment...');
3034
3086
  metricCalculations.forEach(calc => {
3035
3087
  if (calc.isFunnelStage && calc.metric !== MetricEnum.BOUNCE_RATE) {
3088
+ const oldValue = calc.targetValue;
3036
3089
  calc.targetValue += validation.difference / stages.length;
3037
3090
  calc.percentageIncrease = calc.currentValue > 0
3038
3091
  ? ((calc.targetValue - calc.currentValue) / calc.currentValue) * 100
3039
3092
  : 0;
3093
+ console.log(` Adjusted ${calc.metric}: ${oldValue.toFixed(2)} → ${calc.targetValue.toFixed(2)}`);
3040
3094
  }
3041
3095
  });
3042
3096
  adjustmentApplied = validation.difference;
3097
+ console.log(' Total Adjustment Applied:', adjustmentApplied.toFixed(2));
3098
+ }
3099
+ else {
3100
+ console.log('\nāœ… No adjustment needed - within tolerance');
3043
3101
  }
3102
+ console.log('\nšŸ === REVERSE REVENUE CALCULATOR END ===\n');
3044
3103
  return {
3045
3104
  revenueTarget,
3046
3105
  revenuePercentageIncrease,
@@ -3051,11 +3110,28 @@ function calculateMetricTargetsFromRevenueReverse(revenueTarget, priorYearRevenu
3051
3110
  const DERIVED_METRICS = new Set([
3052
3111
  MetricEnum.REVENUE_PER_PRODUCT_VIEW,
3053
3112
  MetricEnum.REVENUE_PER_ADD_TO_CART,
3054
- MetricEnum.REVENUE_PER_CHECKOUT
3113
+ MetricEnum.REVENUE_PER_CHECKOUT,
3114
+ MetricEnum.AVERAGE_ORDER_VALUE
3115
+ ]);
3116
+ const RATE_METRICS = new Set([
3117
+ MetricEnum.PRODUCT_VIEW_RATE,
3118
+ MetricEnum.VIEW_TO_PRODUCT_VIEW_CONVERSION_RATE,
3119
+ MetricEnum.ACTIVE_USER_ADD_TO_CART_RATE,
3120
+ MetricEnum.ADD_TO_CART_RATE,
3121
+ MetricEnum.PRODUCT_VIEW_TO_CART_CONVERSION_RATE,
3122
+ MetricEnum.ACTIVE_USER_CHECKOUT_RATE,
3123
+ MetricEnum.CART_TO_CHECKOUT_CONVERSION_RATE,
3124
+ MetricEnum.PRODUCT_VIEW_CONVERSION_RATE,
3125
+ MetricEnum.ADD_TO_CART_CONVERSION_RATE,
3126
+ MetricEnum.CHECKOUT_CONVERSION_RATE,
3127
+ MetricEnum.ECOMMERCE_CONVERSION_RATE
3055
3128
  ]);
3056
3129
  function isDerivedMetric(metric) {
3057
3130
  return DERIVED_METRICS.has(metric);
3058
3131
  }
3132
+ function isRateMetric(metric) {
3133
+ return RATE_METRICS.has(metric);
3134
+ }
3059
3135
  function validateRevenueTarget(targetRevenue, calculations, baselineValues, funnelRatios) {
3060
3136
  const stages = calculations
3061
3137
  .filter(c => c.isFunnelStage)
@@ -56470,7 +56546,7 @@ function FunnelMetricsVisualizationComponent_For_4_Conditional_17_Template(rf, c
56470
56546
  i0.ɵɵadvance();
56471
56547
  i0.ɵɵproperty("ngClass", ctx_r1.projectedValueClasses());
56472
56548
  i0.ɵɵadvance();
56473
- i0.ɵɵtextInterpolate1(" ", ctx_r1.formatMetricValue(stage_r1.pacingInfo.projectedValue, stage_r1.stageMetric.metric), " ");
56549
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.formatMetricValue(stage_r1.pacingInfo.projectedValue, stage_r1.stageMetric.metric, false), " ");
56474
56550
  } }
56475
56551
  function FunnelMetricsVisualizationComponent_For_4_Conditional_23_For_6_Conditional_5_Template(rf, ctx) { if (rf & 1) {
56476
56552
  i0.ɵɵelementStart(0, "button", 24);
@@ -56500,7 +56576,7 @@ function FunnelMetricsVisualizationComponent_For_4_Conditional_23_For_6_Conditio
56500
56576
  i0.ɵɵadvance();
56501
56577
  i0.ɵɵproperty("ngClass", ctx_r1.relatedLabelClasses());
56502
56578
  i0.ɵɵadvance();
56503
- i0.ɵɵtextInterpolate1("Proj: ", ctx_r1.formatMetricValue(metric_r3.pacingInfo.projectedValue, metric_r3.calc.metric));
56579
+ i0.ɵɵtextInterpolate1("Proj: ", ctx_r1.formatMetricValue(metric_r3.pacingInfo.projectedValue, metric_r3.calc.metric, false));
56504
56580
  } }
56505
56581
  function FunnelMetricsVisualizationComponent_For_4_Conditional_23_For_6_Template(rf, ctx) { if (rf & 1) {
56506
56582
  i0.ɵɵelementStart(0, "div", 20)(1, "div", 21)(2, "div", 22)(3, "p", 23);
@@ -56737,12 +56813,14 @@ class FunnelMetricsVisualizationComponent {
56737
56813
  formatPercentage(value, decimals) {
56738
56814
  return formatPercentage(value, decimals);
56739
56815
  }
56740
- formatMetricValue(value, metric) {
56816
+ formatMetricValue(value, metric, fromUiData = true) {
56741
56817
  if (metric.includes('REVENUE')) {
56742
56818
  return formatCurrency(value);
56743
56819
  }
56744
- if (metric.includes('RATE') || metric.includes('CONVERSION')) {
56745
- return formatPercentage(value, 2);
56820
+ const numberType = MetricEnumUtil.numberType(metric);
56821
+ if (numberType === NumberTypeEnum.PERCENTAGE) {
56822
+ const displayValue = fromUiData ? value * 100 : value;
56823
+ return formatPercentage(displayValue, 2);
56746
56824
  }
56747
56825
  return formatNumber(value);
56748
56826
  }
@@ -56779,140 +56857,140 @@ class FunnelMetricsVisualizationComponent {
56779
56857
  standalone: true,
56780
56858
  imports: [CommonModule, PacingStatusBadgeComponent, TooltipDirective, TooltipContainerComponent],
56781
56859
  changeDetection: ChangeDetectionStrategy.OnPush,
56782
- template: `
56783
- <div class="space-y-6">
56784
- <symphiq-tooltip-container />
56785
- <div class="space-y-8">
56786
- @for (stage of funnelStages(); track stage.stageMetric.metric) {
56787
- <div [ngClass]="stageCardClasses()" class="rounded-xl p-6 border-2 transition-all duration-200">
56788
- <div class="flex items-start justify-between mb-4">
56789
- <div class="flex-1">
56790
- <div class="flex items-center gap-2 mb-1">
56791
- <h3 [ngClass]="stageTitleClasses()" class="text-lg font-bold leading-tight m-0">
56792
- {{ getMetricTitle(stage.stageMetric) }}
56793
- </h3>
56794
- @if (stage.stageMetric.description) {
56795
- <button
56796
- type="button"
56797
- [ngClass]="infoIconClasses()"
56798
- class="flex-shrink-0 w-6 h-6 rounded-full inline-flex items-center justify-center transition-colors"
56799
- [libSymphiqTooltip]="getMarkdownTooltipContent(stage.stageMetric.description, getMetricTitle(stage.stageMetric))"
56800
- tooltipType="markdown"
56801
- tooltipPosition="right">
56802
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
56803
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
56804
- </svg>
56805
- </button>
56806
- }
56807
- </div>
56808
- </div>
56809
- <div class="flex items-center gap-3">
56810
- @if (stage.pacingInfo) {
56811
- <symphiq-pacing-status-badge
56812
- [viewMode]="viewMode()"
56813
- [pacingPercentage]="stage.pacingInfo.pacingPercentage"
56814
- [status]="stage.pacingInfo.status"
56815
- />
56816
- }
56817
- <div [ngClass]="percentageBadgeClasses()" class="px-4 py-2 rounded-lg font-bold text-sm">
56818
- +{{ formatPercentage(stage.stageMetric.percentageIncrease, 1) }}
56819
- </div>
56820
- </div>
56821
- </div>
56822
-
56823
- <div class="grid grid-cols-3 gap-4 mb-4">
56824
- <div>
56825
- <p [ngClass]="labelClasses()" class="text-xs font-medium uppercase tracking-wider mb-1">
56826
- {{ priorYear() }} Actual
56827
- </p>
56828
- <p [ngClass]="valueClasses()" class="text-2xl font-bold">
56829
- {{ formatMetricValue(stage.stageMetric.currentValue, stage.stageMetric.metric) }}
56830
- </p>
56831
- </div>
56832
- @if (stage.pacingInfo) {
56833
- <div>
56834
- <p [ngClass]="labelClasses()" class="text-xs font-medium uppercase tracking-wider mb-1">
56835
- {{ currentYear() }} Projected
56836
- </p>
56837
- <p [ngClass]="projectedValueClasses()" class="text-2xl font-bold">
56838
- {{ formatMetricValue(stage.pacingInfo.projectedValue, stage.stageMetric.metric) }}
56839
- </p>
56840
- </div>
56841
- }
56842
- <div>
56843
- <p [ngClass]="labelClasses()" class="text-xs font-medium uppercase tracking-wider mb-1">
56844
- {{ currentYear() }} Target
56845
- </p>
56846
- <p [ngClass]="targetValueClasses()" class="text-2xl font-bold">
56847
- {{ formatMetricValue(stage.stageMetric.targetValue, stage.stageMetric.metric) }}
56848
- </p>
56849
- </div>
56850
- </div>
56851
-
56852
- @if (stage.relatedMetrics.length > 0) {
56853
- <div [ngClass]="dividerClasses()" class="my-4"></div>
56854
-
56855
- <div class="space-y-2">
56856
- <p [ngClass]="relatedTitleClasses()" class="text-xs font-semibold uppercase tracking-wider mb-3">
56857
- Related Metrics
56858
- </p>
56859
- <div class="grid gap-2">
56860
- @for (metric of stage.relatedMetrics; track metric.calc.metric) {
56861
- <div [ngClass]="relatedMetricCardClasses()" class="p-3 rounded-lg">
56862
- <div class="flex items-center justify-between mb-2">
56863
- <div class="flex items-center gap-2 flex-1">
56864
- <p [ngClass]="relatedMetricNameClasses()" class="text-sm font-semibold leading-tight">
56865
- {{ getMetricTitle(metric.calc) }}
56866
- </p>
56867
- @if (metric.calc.description) {
56868
- <button
56869
- type="button"
56870
- [ngClass]="infoIconClasses()"
56871
- class="flex-shrink-0 w-5 h-5 rounded-full inline-flex items-center justify-center transition-colors"
56872
- [libSymphiqTooltip]="getMarkdownTooltipContent(metric.calc.description, getMetricTitle(metric.calc))"
56873
- tooltipType="markdown"
56874
- tooltipPosition="right">
56875
- <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
56876
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
56877
- </svg>
56878
- </button>
56879
- }
56880
- </div>
56881
- <div class="flex items-center gap-2">
56882
- @if (metric.pacingInfo) {
56883
- <symphiq-pacing-status-badge
56884
- [viewMode]="viewMode()"
56885
- [pacingPercentage]="metric.pacingInfo.pacingPercentage"
56886
- [status]="metric.pacingInfo.status"
56887
- />
56888
- }
56889
- <span [ngClass]="relatedPercentageBadgeClasses()" class="px-2 py-1 rounded text-xs font-bold">
56890
- +{{ formatPercentage(metric.calc.percentageIncrease, 1) }}
56891
- </span>
56892
- </div>
56893
- </div>
56894
- <div class="grid gap-2 text-xs" [ngClass]="metric.pacingInfo ? 'grid-cols-3' : 'grid-cols-2'">
56895
- <div>
56896
- <p [ngClass]="relatedLabelClasses()">{{ priorYear() }}: {{ formatMetricValue(metric.calc.currentValue, metric.calc.metric) }}</p>
56897
- </div>
56898
- @if (metric.pacingInfo) {
56899
- <div>
56900
- <p [ngClass]="relatedLabelClasses()">Proj: {{ formatMetricValue(metric.pacingInfo.projectedValue, metric.calc.metric) }}</p>
56901
- </div>
56902
- }
56903
- <div>
56904
- <p [ngClass]="relatedLabelClasses()">Target: {{ formatMetricValue(metric.calc.targetValue, metric.calc.metric) }}</p>
56905
- </div>
56906
- </div>
56907
- </div>
56908
- }
56909
- </div>
56910
- </div>
56911
- }
56912
- </div>
56913
- }
56914
- </div>
56915
- </div>
56860
+ template: `
56861
+ <div class="space-y-6">
56862
+ <symphiq-tooltip-container />
56863
+ <div class="space-y-8">
56864
+ @for (stage of funnelStages(); track stage.stageMetric.metric) {
56865
+ <div [ngClass]="stageCardClasses()" class="rounded-xl p-6 border-2 transition-all duration-200">
56866
+ <div class="flex items-start justify-between mb-4">
56867
+ <div class="flex-1">
56868
+ <div class="flex items-center gap-2 mb-1">
56869
+ <h3 [ngClass]="stageTitleClasses()" class="text-lg font-bold leading-tight m-0">
56870
+ {{ getMetricTitle(stage.stageMetric) }}
56871
+ </h3>
56872
+ @if (stage.stageMetric.description) {
56873
+ <button
56874
+ type="button"
56875
+ [ngClass]="infoIconClasses()"
56876
+ class="flex-shrink-0 w-6 h-6 rounded-full inline-flex items-center justify-center transition-colors"
56877
+ [libSymphiqTooltip]="getMarkdownTooltipContent(stage.stageMetric.description, getMetricTitle(stage.stageMetric))"
56878
+ tooltipType="markdown"
56879
+ tooltipPosition="right">
56880
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
56881
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
56882
+ </svg>
56883
+ </button>
56884
+ }
56885
+ </div>
56886
+ </div>
56887
+ <div class="flex items-center gap-3">
56888
+ @if (stage.pacingInfo) {
56889
+ <symphiq-pacing-status-badge
56890
+ [viewMode]="viewMode()"
56891
+ [pacingPercentage]="stage.pacingInfo.pacingPercentage"
56892
+ [status]="stage.pacingInfo.status"
56893
+ />
56894
+ }
56895
+ <div [ngClass]="percentageBadgeClasses()" class="px-4 py-2 rounded-lg font-bold text-sm">
56896
+ +{{ formatPercentage(stage.stageMetric.percentageIncrease, 1) }}
56897
+ </div>
56898
+ </div>
56899
+ </div>
56900
+
56901
+ <div class="grid grid-cols-3 gap-4 mb-4">
56902
+ <div>
56903
+ <p [ngClass]="labelClasses()" class="text-xs font-medium uppercase tracking-wider mb-1">
56904
+ {{ priorYear() }} Actual
56905
+ </p>
56906
+ <p [ngClass]="valueClasses()" class="text-2xl font-bold">
56907
+ {{ formatMetricValue(stage.stageMetric.currentValue, stage.stageMetric.metric) }}
56908
+ </p>
56909
+ </div>
56910
+ @if (stage.pacingInfo) {
56911
+ <div>
56912
+ <p [ngClass]="labelClasses()" class="text-xs font-medium uppercase tracking-wider mb-1">
56913
+ {{ currentYear() }} Projected
56914
+ </p>
56915
+ <p [ngClass]="projectedValueClasses()" class="text-2xl font-bold">
56916
+ {{ formatMetricValue(stage.pacingInfo.projectedValue, stage.stageMetric.metric, false) }}
56917
+ </p>
56918
+ </div>
56919
+ }
56920
+ <div>
56921
+ <p [ngClass]="labelClasses()" class="text-xs font-medium uppercase tracking-wider mb-1">
56922
+ {{ currentYear() }} Target
56923
+ </p>
56924
+ <p [ngClass]="targetValueClasses()" class="text-2xl font-bold">
56925
+ {{ formatMetricValue(stage.stageMetric.targetValue, stage.stageMetric.metric) }}
56926
+ </p>
56927
+ </div>
56928
+ </div>
56929
+
56930
+ @if (stage.relatedMetrics.length > 0) {
56931
+ <div [ngClass]="dividerClasses()" class="my-4"></div>
56932
+
56933
+ <div class="space-y-2">
56934
+ <p [ngClass]="relatedTitleClasses()" class="text-xs font-semibold uppercase tracking-wider mb-3">
56935
+ Related Metrics
56936
+ </p>
56937
+ <div class="grid gap-2">
56938
+ @for (metric of stage.relatedMetrics; track metric.calc.metric) {
56939
+ <div [ngClass]="relatedMetricCardClasses()" class="p-3 rounded-lg">
56940
+ <div class="flex items-center justify-between mb-2">
56941
+ <div class="flex items-center gap-2 flex-1">
56942
+ <p [ngClass]="relatedMetricNameClasses()" class="text-sm font-semibold leading-tight">
56943
+ {{ getMetricTitle(metric.calc) }}
56944
+ </p>
56945
+ @if (metric.calc.description) {
56946
+ <button
56947
+ type="button"
56948
+ [ngClass]="infoIconClasses()"
56949
+ class="flex-shrink-0 w-5 h-5 rounded-full inline-flex items-center justify-center transition-colors"
56950
+ [libSymphiqTooltip]="getMarkdownTooltipContent(metric.calc.description, getMetricTitle(metric.calc))"
56951
+ tooltipType="markdown"
56952
+ tooltipPosition="right">
56953
+ <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
56954
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
56955
+ </svg>
56956
+ </button>
56957
+ }
56958
+ </div>
56959
+ <div class="flex items-center gap-2">
56960
+ @if (metric.pacingInfo) {
56961
+ <symphiq-pacing-status-badge
56962
+ [viewMode]="viewMode()"
56963
+ [pacingPercentage]="metric.pacingInfo.pacingPercentage"
56964
+ [status]="metric.pacingInfo.status"
56965
+ />
56966
+ }
56967
+ <span [ngClass]="relatedPercentageBadgeClasses()" class="px-2 py-1 rounded text-xs font-bold">
56968
+ +{{ formatPercentage(metric.calc.percentageIncrease, 1) }}
56969
+ </span>
56970
+ </div>
56971
+ </div>
56972
+ <div class="grid gap-2 text-xs" [ngClass]="metric.pacingInfo ? 'grid-cols-3' : 'grid-cols-2'">
56973
+ <div>
56974
+ <p [ngClass]="relatedLabelClasses()">{{ priorYear() }}: {{ formatMetricValue(metric.calc.currentValue, metric.calc.metric) }}</p>
56975
+ </div>
56976
+ @if (metric.pacingInfo) {
56977
+ <div>
56978
+ <p [ngClass]="relatedLabelClasses()">Proj: {{ formatMetricValue(metric.pacingInfo.projectedValue, metric.calc.metric, false) }}</p>
56979
+ </div>
56980
+ }
56981
+ <div>
56982
+ <p [ngClass]="relatedLabelClasses()">Target: {{ formatMetricValue(metric.calc.targetValue, metric.calc.metric) }}</p>
56983
+ </div>
56984
+ </div>
56985
+ </div>
56986
+ }
56987
+ </div>
56988
+ </div>
56989
+ }
56990
+ </div>
56991
+ }
56992
+ </div>
56993
+ </div>
56916
56994
  `
56917
56995
  }]
56918
56996
  }], null, { viewMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewMode", required: false }] }], calculations: [{ type: i0.Input, args: [{ isSignal: true, alias: "calculations", required: false }] }], pacingMetrics: [{ type: i0.Input, args: [{ isSignal: true, alias: "pacingMetrics", required: false }] }] }); })();
@@ -57924,20 +58002,41 @@ class InitialTargetSettingComponent {
57924
58002
  }, ...(ngDevMode ? [{ debugName: "percentageIncrease" }] : []));
57925
58003
  this.metricCalculations = computed(() => {
57926
58004
  const revenue = this.calculatedRevenue();
57927
- if (revenue <= 0)
58005
+ console.log('=== METRIC CALCULATIONS START ===');
58006
+ console.log('Revenue Target:', revenue);
58007
+ if (revenue <= 0) {
58008
+ console.log('Revenue is <= 0, returning empty array');
57928
58009
  return [];
58010
+ }
57929
58011
  const mode = this.inputMode();
57930
58012
  const mainData = this.mainUiData();
57931
58013
  const metrics = this.funnelMetrics();
58014
+ const priorRevenue = this.priorYearRevenue();
58015
+ console.log('Input Mode:', mode);
58016
+ console.log('Prior Year Revenue:', priorRevenue);
58017
+ console.log('Funnel Metrics Count:', metrics.length);
58018
+ console.log('Main UI Data available:', !!mainData);
58019
+ let result;
57932
58020
  if (mode === 'absolute') {
57933
- return this.revenueCalcService.calculateTargetsFromRevenue(revenue, mainData, metrics).metricCalculations;
58021
+ console.log('Using calculateTargetsFromRevenueWithRatios (NEW REVERSE ALGORITHM)');
58022
+ result = this.revenueCalcService.calculateTargetsFromRevenueWithRatios(revenue, mainData, metrics);
58023
+ console.log('Adjustment Applied:', result.adjustmentApplied);
57934
58024
  }
57935
58025
  else {
57936
58026
  const pct = this.percentageInput();
57937
- if (pct === null)
58027
+ console.log('Percentage Input:', pct);
58028
+ if (pct === null) {
58029
+ console.log('Percentage is null, returning empty array');
57938
58030
  return [];
57939
- return this.revenueCalcService.calculateTargetsFromPercentage(pct, mainData, metrics).metricCalculations;
57940
- }
58031
+ }
58032
+ console.log('Using calculateTargetsFromPercentageWithRatios (NEW REVERSE ALGORITHM)');
58033
+ result = this.revenueCalcService.calculateTargetsFromPercentageWithRatios(pct, mainData, metrics);
58034
+ console.log('Adjustment Applied:', result.adjustmentApplied);
58035
+ }
58036
+ console.log('Metric Calculations Count:', result.metricCalculations.length);
58037
+ console.log('Metric Calculations:', result.metricCalculations);
58038
+ console.log('=== METRIC CALCULATIONS END ===');
58039
+ return result.metricCalculations;
57941
58040
  }, ...(ngDevMode ? [{ debugName: "metricCalculations" }] : []));
57942
58041
  this.isValid = computed(() => {
57943
58042
  return this.calculatedRevenue() > 0 && this.priorYearRevenue() > 0;