@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.
- package/fesm2022/symphiq-components.mjs +246 -147
- package/fesm2022/symphiq-components.mjs.map +1 -1
- package/index.d.ts +17 -17
- package/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
56745
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
58027
|
+
console.log('Percentage Input:', pct);
|
|
58028
|
+
if (pct === null) {
|
|
58029
|
+
console.log('Percentage is null, returning empty array');
|
|
57938
58030
|
return [];
|
|
57939
|
-
|
|
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;
|