@eric-emg/symphiq-components 1.2.0
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/README.md +179 -0
- package/fesm2022/symphiq-components.mjs +3036 -0
- package/fesm2022/symphiq-components.mjs.map +1 -0
- package/index.d.ts +166 -0
- package/index.d.ts.map +1 -0
- package/package.json +55 -0
- package/src/styles.css +1 -0
- package/styles.css +1403 -0
|
@@ -0,0 +1,3036 @@
|
|
|
1
|
+
import { normalizeToV3 } from '@jebgem/model';
|
|
2
|
+
export * from '@jebgem/model';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { Injectable, Component, Input } from '@angular/core';
|
|
5
|
+
import { BehaviorSubject } from 'rxjs';
|
|
6
|
+
import * as i2 from '@angular/common';
|
|
7
|
+
import { CommonModule } from '@angular/common';
|
|
8
|
+
import * as i3 from '@angular/forms';
|
|
9
|
+
import { FormsModule } from '@angular/forms';
|
|
10
|
+
|
|
11
|
+
class FunnelOrderService {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.funnelOrder = [
|
|
14
|
+
{ metric: 'PURCHASE_REVENUE', funnelMetric: 'PURCHASE_REVENUE', funnelInd: 5, relatedInd: 20, isFunnelStage: true },
|
|
15
|
+
{ metric: 'REVENUE_PER_PRODUCT_VIEW', funnelMetric: 'PURCHASE_REVENUE', funnelInd: 5, relatedInd: 21, isFunnelStage: false },
|
|
16
|
+
{ metric: 'REVENUE_PER_ADD_TO_CART', funnelMetric: 'PURCHASE_REVENUE', funnelInd: 5, relatedInd: 22, isFunnelStage: false },
|
|
17
|
+
{ metric: 'REVENUE_PER_CHECKOUT', funnelMetric: 'PURCHASE_REVENUE', funnelInd: 5, relatedInd: 23, isFunnelStage: false },
|
|
18
|
+
{ metric: 'AVERAGE_ORDER_VALUE', funnelMetric: 'PURCHASE_REVENUE', funnelInd: 5, relatedInd: 24, isFunnelStage: false },
|
|
19
|
+
{ metric: 'ECOMMERCE_PURCHASES', funnelMetric: 'ECOMMERCE_PURCHASES', funnelInd: 4, relatedInd: 15, isFunnelStage: true },
|
|
20
|
+
{ metric: 'PRODUCT_VIEW_CONVERSION_RATE', funnelMetric: 'ECOMMERCE_PURCHASES', funnelInd: 4, relatedInd: 16, isFunnelStage: false },
|
|
21
|
+
{ metric: 'ADD_TO_CART_CONVERSION_RATE', funnelMetric: 'ECOMMERCE_PURCHASES', funnelInd: 4, relatedInd: 17, isFunnelStage: false },
|
|
22
|
+
{ metric: 'CHECKOUT_CONVERSION_RATE', funnelMetric: 'ECOMMERCE_PURCHASES', funnelInd: 4, relatedInd: 18, isFunnelStage: false },
|
|
23
|
+
{ metric: 'ECOMMERCE_CONVERSION_RATE', funnelMetric: 'ECOMMERCE_PURCHASES', funnelInd: 4, relatedInd: 19, isFunnelStage: false },
|
|
24
|
+
{ metric: 'CHECKOUTS', funnelMetric: 'CHECKOUTS', funnelInd: 3, relatedInd: 12, isFunnelStage: true },
|
|
25
|
+
{ metric: 'ACTIVE_USER_CHECKOUT_RATE', funnelMetric: 'CHECKOUTS', funnelInd: 3, relatedInd: 13, isFunnelStage: false },
|
|
26
|
+
{ metric: 'CART_TO_CHECKOUT_CONVERSION_RATE', funnelMetric: 'CHECKOUTS', funnelInd: 3, relatedInd: 14, isFunnelStage: false },
|
|
27
|
+
{ metric: 'ADD_TO_CARTS', funnelMetric: 'ADD_TO_CARTS', funnelInd: 2, relatedInd: 8, isFunnelStage: true },
|
|
28
|
+
{ metric: 'ACTIVE_USER_ADD_TO_CART_RATE', funnelMetric: 'ADD_TO_CARTS', funnelInd: 2, relatedInd: 9, isFunnelStage: false },
|
|
29
|
+
{ metric: 'ADD_TO_CART_RATE', funnelMetric: 'ADD_TO_CARTS', funnelInd: 2, relatedInd: 10, isFunnelStage: false },
|
|
30
|
+
{ metric: 'PRODUCT_VIEW_TO_CART_CONVERSION_RATE', funnelMetric: 'ADD_TO_CARTS', funnelInd: 2, relatedInd: 11, isFunnelStage: false },
|
|
31
|
+
{ metric: 'ITEM_VIEW_EVENTS', funnelMetric: 'ITEM_VIEW_EVENTS', funnelInd: 1, relatedInd: 5, isFunnelStage: true },
|
|
32
|
+
{ metric: 'PRODUCT_VIEW_RATE', funnelMetric: 'ITEM_VIEW_EVENTS', funnelInd: 1, relatedInd: 6, isFunnelStage: false },
|
|
33
|
+
{ metric: 'VIEW_TO_PRODUCT_VIEW_CONVERSION_RATE', funnelMetric: 'ITEM_VIEW_EVENTS', funnelInd: 1, relatedInd: 7, isFunnelStage: false },
|
|
34
|
+
{ metric: 'SCREEN_PAGE_VIEWS', funnelMetric: 'SCREEN_PAGE_VIEWS', funnelInd: 0, relatedInd: 0, isFunnelStage: true },
|
|
35
|
+
{ metric: 'ACTIVE_USERS', funnelMetric: 'SCREEN_PAGE_VIEWS', funnelInd: 0, relatedInd: 1, isFunnelStage: false },
|
|
36
|
+
{ metric: 'BOUNCE_RATE', funnelMetric: 'SCREEN_PAGE_VIEWS', funnelInd: 0, relatedInd: 2, isFunnelStage: false },
|
|
37
|
+
{ metric: 'NEW_USERS', funnelMetric: 'SCREEN_PAGE_VIEWS', funnelInd: 0, relatedInd: 3, isFunnelStage: false },
|
|
38
|
+
{ metric: 'SESSIONS', funnelMetric: 'SCREEN_PAGE_VIEWS', funnelInd: 0, relatedInd: 4, isFunnelStage: false },
|
|
39
|
+
];
|
|
40
|
+
this.funnelStageNames = {
|
|
41
|
+
5: 'REVENUE',
|
|
42
|
+
4: 'PURCHASES',
|
|
43
|
+
3: 'CHECKOUTS',
|
|
44
|
+
2: 'ADD TO CARTS',
|
|
45
|
+
1: 'PRODUCT VIEWS',
|
|
46
|
+
0: 'VIEWS'
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
getMetricOrder(metricName) {
|
|
50
|
+
return this.funnelOrder.find(m => m.metric === metricName);
|
|
51
|
+
}
|
|
52
|
+
isFunnelStage(metricName) {
|
|
53
|
+
const order = this.getMetricOrder(metricName);
|
|
54
|
+
return order?.isFunnelStage ?? false;
|
|
55
|
+
}
|
|
56
|
+
getFunnelStageName(funnelInd) {
|
|
57
|
+
return this.funnelStageNames[funnelInd] || 'UNKNOWN';
|
|
58
|
+
}
|
|
59
|
+
getFunnelStageForMetric(metricName) {
|
|
60
|
+
const order = this.getMetricOrder(metricName);
|
|
61
|
+
return order ? this.getFunnelStageName(order.funnelInd) : null;
|
|
62
|
+
}
|
|
63
|
+
sortMetricsByFunnelOrder(metrics) {
|
|
64
|
+
return [...metrics].sort((a, b) => {
|
|
65
|
+
const orderA = this.getMetricOrder(a.metric);
|
|
66
|
+
const orderB = this.getMetricOrder(b.metric);
|
|
67
|
+
if (!orderA && !orderB)
|
|
68
|
+
return 0;
|
|
69
|
+
if (!orderA)
|
|
70
|
+
return 1;
|
|
71
|
+
if (!orderB)
|
|
72
|
+
return -1;
|
|
73
|
+
if (orderA.funnelInd !== orderB.funnelInd) {
|
|
74
|
+
return orderA.funnelInd - orderB.funnelInd;
|
|
75
|
+
}
|
|
76
|
+
return orderA.relatedInd - orderB.relatedInd;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
getMetricsForFunnelStage(metrics, funnelStageName) {
|
|
80
|
+
const funnelInd = Object.entries(this.funnelStageNames)
|
|
81
|
+
.find(([_, name]) => name === funnelStageName)?.[0];
|
|
82
|
+
if (!funnelInd)
|
|
83
|
+
return [];
|
|
84
|
+
const funnelIndNum = parseInt(funnelInd);
|
|
85
|
+
return metrics.filter(metric => {
|
|
86
|
+
const order = this.getMetricOrder(metric.metric);
|
|
87
|
+
return order?.funnelInd === funnelIndNum;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
static { this.ɵfac = function FunnelOrderService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FunnelOrderService)(); }; }
|
|
91
|
+
static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: FunnelOrderService, factory: FunnelOrderService.ɵfac, providedIn: 'root' }); }
|
|
92
|
+
}
|
|
93
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FunnelOrderService, [{
|
|
94
|
+
type: Injectable,
|
|
95
|
+
args: [{
|
|
96
|
+
providedIn: 'root'
|
|
97
|
+
}]
|
|
98
|
+
}], null, null); })();
|
|
99
|
+
|
|
100
|
+
class ModalService {
|
|
101
|
+
constructor() {
|
|
102
|
+
this.modalState = new BehaviorSubject({ type: null, data: null });
|
|
103
|
+
this.modalState$ = this.modalState.asObservable();
|
|
104
|
+
}
|
|
105
|
+
openInsightModal(insight) {
|
|
106
|
+
this.modalState.next({ type: 'insight', data: insight });
|
|
107
|
+
}
|
|
108
|
+
openMetricModal(metric) {
|
|
109
|
+
this.modalState.next({ type: 'metric', data: metric });
|
|
110
|
+
}
|
|
111
|
+
closeModal() {
|
|
112
|
+
this.modalState.next({ type: null, data: null });
|
|
113
|
+
}
|
|
114
|
+
static { this.ɵfac = function ModalService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ModalService)(); }; }
|
|
115
|
+
static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: ModalService, factory: ModalService.ɵfac, providedIn: 'root' }); }
|
|
116
|
+
}
|
|
117
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ModalService, [{
|
|
118
|
+
type: Injectable,
|
|
119
|
+
args: [{
|
|
120
|
+
providedIn: 'root'
|
|
121
|
+
}]
|
|
122
|
+
}], null, null); })();
|
|
123
|
+
|
|
124
|
+
function OverallAssessmentComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
125
|
+
i0.ɵɵdomElementStart(0, "div", 9)(1, "div", 35)(2, "div", 36)(3, "div", 37)(4, "div", 38);
|
|
126
|
+
i0.ɵɵnamespaceSVG();
|
|
127
|
+
i0.ɵɵdomElementStart(5, "svg", 39);
|
|
128
|
+
i0.ɵɵdomElement(6, "path", 40);
|
|
129
|
+
i0.ɵɵdomElementEnd()();
|
|
130
|
+
i0.ɵɵnamespaceHTML();
|
|
131
|
+
i0.ɵɵdomElementStart(7, "div")(8, "h3", 41);
|
|
132
|
+
i0.ɵɵtext(9, "Your Revenue");
|
|
133
|
+
i0.ɵɵdomElementEnd();
|
|
134
|
+
i0.ɵɵdomElementStart(10, "p", 42);
|
|
135
|
+
i0.ɵɵtext(11);
|
|
136
|
+
i0.ɵɵdomElementEnd()()();
|
|
137
|
+
i0.ɵɵdomElementStart(12, "div", 43)(13, "div", 44);
|
|
138
|
+
i0.ɵɵnamespaceSVG();
|
|
139
|
+
i0.ɵɵdomElementStart(14, "svg", 45);
|
|
140
|
+
i0.ɵɵdomElement(15, "path", 46);
|
|
141
|
+
i0.ɵɵdomElementEnd();
|
|
142
|
+
i0.ɵɵnamespaceHTML();
|
|
143
|
+
i0.ɵɵdomElementStart(16, "span", 47);
|
|
144
|
+
i0.ɵɵtext(17);
|
|
145
|
+
i0.ɵɵdomElementEnd();
|
|
146
|
+
i0.ɵɵdomElementStart(18, "span", 48);
|
|
147
|
+
i0.ɵɵtext(19, "vs last year");
|
|
148
|
+
i0.ɵɵdomElementEnd()();
|
|
149
|
+
i0.ɵɵdomElement(20, "div", 49);
|
|
150
|
+
i0.ɵɵdomElementStart(21, "div")(22, "span", 48);
|
|
151
|
+
i0.ɵɵtext(23, "Target: ");
|
|
152
|
+
i0.ɵɵdomElementEnd();
|
|
153
|
+
i0.ɵɵdomElementStart(24, "span", 50);
|
|
154
|
+
i0.ɵɵtext(25);
|
|
155
|
+
i0.ɵɵdomElementEnd()()()();
|
|
156
|
+
i0.ɵɵdomElementStart(26, "div", 51)(27, "div", 6);
|
|
157
|
+
i0.ɵɵtext(28, "Status");
|
|
158
|
+
i0.ɵɵdomElementEnd();
|
|
159
|
+
i0.ɵɵdomElementStart(29, "div", 52);
|
|
160
|
+
i0.ɵɵtext(30);
|
|
161
|
+
i0.ɵɵdomElementEnd();
|
|
162
|
+
i0.ɵɵdomElementStart(31, "div", 53);
|
|
163
|
+
i0.ɵɵtext(32);
|
|
164
|
+
i0.ɵɵdomElementEnd()()()();
|
|
165
|
+
} if (rf & 2) {
|
|
166
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
167
|
+
i0.ɵɵadvance(11);
|
|
168
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatRevenue(ctx_r0.revenueMetric.currentValue || 0));
|
|
169
|
+
i0.ɵɵadvance(6);
|
|
170
|
+
i0.ɵɵtextInterpolate1("", ctx_r0.formatPercent(ctx_r0.revenueMetric.trendPercent || 0), "%");
|
|
171
|
+
i0.ɵɵadvance(8);
|
|
172
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatRevenue(ctx_r0.revenueMetric.targetValue || 0));
|
|
173
|
+
i0.ɵɵadvance(4);
|
|
174
|
+
i0.ɵɵclassMap(ctx_r0.getRevenueStatusClass());
|
|
175
|
+
i0.ɵɵadvance();
|
|
176
|
+
i0.ɵɵtextInterpolate1(" ", (ctx_r0.revenueMetric.status || "ON_TRACK").replace("_", " "), " ");
|
|
177
|
+
i0.ɵɵadvance(2);
|
|
178
|
+
i0.ɵɵtextInterpolate(ctx_r0.revenueMetric.description);
|
|
179
|
+
} }
|
|
180
|
+
class OverallAssessmentComponent {
|
|
181
|
+
formatRevenue(value) {
|
|
182
|
+
return '$' + value.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
|
183
|
+
}
|
|
184
|
+
formatPercent(value) {
|
|
185
|
+
return value > 0 ? '+' + value.toFixed(1) : value.toFixed(1);
|
|
186
|
+
}
|
|
187
|
+
getRevenueStatusClass() {
|
|
188
|
+
if (!this.revenueMetric)
|
|
189
|
+
return 'bg-slate-500/20 text-slate-400';
|
|
190
|
+
switch (this.revenueMetric.status || 'ON_TRACK') {
|
|
191
|
+
case 'OVERACHIEVING':
|
|
192
|
+
return 'bg-emerald-500/20 text-emerald-400';
|
|
193
|
+
case 'ON_TRACK':
|
|
194
|
+
return 'bg-blue-500/20 text-blue-400';
|
|
195
|
+
case 'AT_RISK':
|
|
196
|
+
return 'bg-amber-500/20 text-amber-400';
|
|
197
|
+
default:
|
|
198
|
+
return 'bg-slate-500/20 text-slate-400';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
getStatusBadgeClass() {
|
|
202
|
+
const baseClass = 'px-4 py-2 rounded-xl';
|
|
203
|
+
const status = this.assessment.overallStatus || 'ON_TRACK';
|
|
204
|
+
switch (status) {
|
|
205
|
+
case 'ON_TRACK':
|
|
206
|
+
return `${baseClass} bg-blue-500/20 text-blue-400 border border-blue-500/30`;
|
|
207
|
+
case 'OVERACHIEVING':
|
|
208
|
+
return `${baseClass} bg-emerald-500/20 text-emerald-400 border border-emerald-500/30`;
|
|
209
|
+
case 'AT_RISK':
|
|
210
|
+
return `${baseClass} bg-amber-500/20 text-amber-400 border border-amber-500/30`;
|
|
211
|
+
default:
|
|
212
|
+
return `${baseClass} bg-slate-500/20 text-slate-400 border border-slate-500/30`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
getStatusLabel() {
|
|
216
|
+
return (this.assessment.overallStatus || '').replace(/_/g, ' ');
|
|
217
|
+
}
|
|
218
|
+
getGradeBadgeClass() {
|
|
219
|
+
const grade = this.assessment.grade || 'B';
|
|
220
|
+
const baseClass = 'px-6 py-3 rounded-xl text-center border-2';
|
|
221
|
+
switch (grade) {
|
|
222
|
+
case 'A':
|
|
223
|
+
return `${baseClass} bg-emerald-500/20 text-emerald-400 border-emerald-500/50`;
|
|
224
|
+
case 'B':
|
|
225
|
+
return `${baseClass} bg-blue-500/20 text-blue-400 border-blue-500/50`;
|
|
226
|
+
case 'C':
|
|
227
|
+
return `${baseClass} bg-amber-500/20 text-amber-400 border-amber-500/50`;
|
|
228
|
+
case 'D':
|
|
229
|
+
case 'F':
|
|
230
|
+
default:
|
|
231
|
+
return `${baseClass} bg-red-500/20 text-red-400 border-red-500/50`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
static { this.ɵfac = function OverallAssessmentComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || OverallAssessmentComponent)(); }; }
|
|
235
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: OverallAssessmentComponent, selectors: [["symphiq-funnel-analysis-overall-assessment"]], inputs: { assessment: "assessment", revenueMetric: "revenueMetric" }, decls: 59, vars: 13, consts: [[1, "bg-gradient-to-br", "from-slate-800", "to-slate-900", "rounded-2xl", "p-8", "border", "border-slate-700", "shadow-xl"], [1, "flex", "items-start", "justify-between", "mb-6"], [1, "text-3xl", "font-bold", "text-white", "mb-2"], [1, "text-slate-400"], [1, "flex", "items-center", "gap-4"], [1, "text-center"], [1, "text-xs", "font-semibold", "text-slate-400", "uppercase", "tracking-wider", "mb-1"], [1, "text-lg", "font-bold"], [1, "text-4xl", "font-bold"], [1, "bg-gradient-to-r", "from-emerald-500/20", "to-blue-500/20", "rounded-xl", "p-6", "mb-6", "border-2", "border-emerald-500/40"], [1, "bg-slate-900/50", "rounded-xl", "p-6", "mb-6", "border", "border-slate-700"], [1, "text-slate-300", "leading-relaxed"], [1, "grid", "grid-cols-1", "md:grid-cols-2", "gap-6"], [1, "bg-emerald-500/10", "rounded-xl", "p-6", "border", "border-emerald-500/30"], [1, "flex", "items-center", "gap-3", "mb-3"], [1, "w-10", "h-10", "bg-emerald-500/20", "rounded-lg", "flex", "items-center", "justify-center"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-6", "h-6", "text-emerald-400"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"], [1, "text-lg", "font-semibold", "text-emerald-400"], [1, "text-sm", "text-slate-300", "leading-relaxed"], [1, "bg-amber-500/10", "rounded-xl", "p-6", "border", "border-amber-500/30"], [1, "w-10", "h-10", "bg-amber-500/20", "rounded-lg", "flex", "items-center", "justify-center"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-6", "h-6", "text-amber-400"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"], [1, "text-lg", "font-semibold", "text-amber-400"], [1, "bg-blue-500/10", "rounded-xl", "p-6", "border", "border-blue-500/30"], [1, "w-10", "h-10", "bg-blue-500/20", "rounded-lg", "flex", "items-center", "justify-center"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-6", "h-6", "text-blue-400"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"], [1, "text-lg", "font-semibold", "text-blue-400"], [1, "bg-purple-500/10", "rounded-xl", "p-6", "border", "border-purple-500/30"], [1, "w-10", "h-10", "bg-purple-500/20", "rounded-lg", "flex", "items-center", "justify-center"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-6", "h-6", "text-purple-400"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"], [1, "text-lg", "font-semibold", "text-purple-400"], [1, "flex", "items-center", "justify-between"], [1, "flex-1"], [1, "flex", "items-center", "gap-3", "mb-2"], [1, "w-12", "h-12", "bg-emerald-500/30", "rounded-lg", "flex", "items-center", "justify-center"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-7", "h-7", "text-emerald-400"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"], [1, "text-sm", "font-semibold", "text-slate-400", "uppercase", "tracking-wider"], [1, "text-4xl", "font-bold", "text-white"], [1, "flex", "items-center", "gap-4", "mt-3"], [1, "flex", "items-center", "gap-2"], ["fill", "currentColor", "viewBox", "0 0 20 20", 1, "w-5", "h-5", "text-emerald-400"], ["fill-rule", "evenodd", "d", "M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z", "clip-rule", "evenodd"], [1, "text-emerald-400", "font-semibold", "text-lg"], [1, "text-slate-400", "text-sm"], [1, "h-6", "w-px", "bg-slate-600"], [1, "text-white", "font-semibold"], [1, "text-right"], [1, "inline-block", "px-4", "py-2", "rounded-lg", "font-bold", "text-lg"], [1, "mt-2", "text-sm", "text-slate-400"]], template: function OverallAssessmentComponent_Template(rf, ctx) { if (rf & 1) {
|
|
236
|
+
i0.ɵɵdomElementStart(0, "div", 0)(1, "div", 1)(2, "div")(3, "h2", 2);
|
|
237
|
+
i0.ɵɵtext(4, "Overall Performance");
|
|
238
|
+
i0.ɵɵdomElementEnd();
|
|
239
|
+
i0.ɵɵdomElementStart(5, "p", 3);
|
|
240
|
+
i0.ɵɵtext(6);
|
|
241
|
+
i0.ɵɵdomElementEnd()();
|
|
242
|
+
i0.ɵɵdomElementStart(7, "div", 4)(8, "div", 5)(9, "div", 6);
|
|
243
|
+
i0.ɵɵtext(10, "Status");
|
|
244
|
+
i0.ɵɵdomElementEnd();
|
|
245
|
+
i0.ɵɵdomElementStart(11, "div", 7);
|
|
246
|
+
i0.ɵɵtext(12);
|
|
247
|
+
i0.ɵɵdomElementEnd()();
|
|
248
|
+
i0.ɵɵdomElementStart(13, "div")(14, "div", 6);
|
|
249
|
+
i0.ɵɵtext(15, "Grade");
|
|
250
|
+
i0.ɵɵdomElementEnd();
|
|
251
|
+
i0.ɵɵdomElementStart(16, "div", 8);
|
|
252
|
+
i0.ɵɵtext(17);
|
|
253
|
+
i0.ɵɵdomElementEnd()()()();
|
|
254
|
+
i0.ɵɵconditionalCreate(18, OverallAssessmentComponent_Conditional_18_Template, 33, 7, "div", 9);
|
|
255
|
+
i0.ɵɵdomElementStart(19, "div", 10)(20, "p", 11);
|
|
256
|
+
i0.ɵɵtext(21);
|
|
257
|
+
i0.ɵɵdomElementEnd()();
|
|
258
|
+
i0.ɵɵdomElementStart(22, "div", 12)(23, "div", 13)(24, "div", 14)(25, "div", 15);
|
|
259
|
+
i0.ɵɵnamespaceSVG();
|
|
260
|
+
i0.ɵɵdomElementStart(26, "svg", 16);
|
|
261
|
+
i0.ɵɵdomElement(27, "path", 17);
|
|
262
|
+
i0.ɵɵdomElementEnd()();
|
|
263
|
+
i0.ɵɵnamespaceHTML();
|
|
264
|
+
i0.ɵɵdomElementStart(28, "h3", 18);
|
|
265
|
+
i0.ɵɵtext(29, "Key Strengths");
|
|
266
|
+
i0.ɵɵdomElementEnd()();
|
|
267
|
+
i0.ɵɵdomElementStart(30, "p", 19);
|
|
268
|
+
i0.ɵɵtext(31);
|
|
269
|
+
i0.ɵɵdomElementEnd()();
|
|
270
|
+
i0.ɵɵdomElementStart(32, "div", 20)(33, "div", 14)(34, "div", 21);
|
|
271
|
+
i0.ɵɵnamespaceSVG();
|
|
272
|
+
i0.ɵɵdomElementStart(35, "svg", 22);
|
|
273
|
+
i0.ɵɵdomElement(36, "path", 23);
|
|
274
|
+
i0.ɵɵdomElementEnd()();
|
|
275
|
+
i0.ɵɵnamespaceHTML();
|
|
276
|
+
i0.ɵɵdomElementStart(37, "h3", 24);
|
|
277
|
+
i0.ɵɵtext(38, "Areas for Improvement");
|
|
278
|
+
i0.ɵɵdomElementEnd()();
|
|
279
|
+
i0.ɵɵdomElementStart(39, "p", 19);
|
|
280
|
+
i0.ɵɵtext(40);
|
|
281
|
+
i0.ɵɵdomElementEnd()();
|
|
282
|
+
i0.ɵɵdomElementStart(41, "div", 25)(42, "div", 14)(43, "div", 26);
|
|
283
|
+
i0.ɵɵnamespaceSVG();
|
|
284
|
+
i0.ɵɵdomElementStart(44, "svg", 27);
|
|
285
|
+
i0.ɵɵdomElement(45, "path", 28);
|
|
286
|
+
i0.ɵɵdomElementEnd()();
|
|
287
|
+
i0.ɵɵnamespaceHTML();
|
|
288
|
+
i0.ɵɵdomElementStart(46, "h3", 29);
|
|
289
|
+
i0.ɵɵtext(47, "Prior Year Trend");
|
|
290
|
+
i0.ɵɵdomElementEnd()();
|
|
291
|
+
i0.ɵɵdomElementStart(48, "p", 19);
|
|
292
|
+
i0.ɵɵtext(49);
|
|
293
|
+
i0.ɵɵdomElementEnd()();
|
|
294
|
+
i0.ɵɵdomElementStart(50, "div", 30)(51, "div", 14)(52, "div", 31);
|
|
295
|
+
i0.ɵɵnamespaceSVG();
|
|
296
|
+
i0.ɵɵdomElementStart(53, "svg", 32);
|
|
297
|
+
i0.ɵɵdomElement(54, "path", 33);
|
|
298
|
+
i0.ɵɵdomElementEnd()();
|
|
299
|
+
i0.ɵɵnamespaceHTML();
|
|
300
|
+
i0.ɵɵdomElementStart(55, "h3", 34);
|
|
301
|
+
i0.ɵɵtext(56, "Recommended Actions");
|
|
302
|
+
i0.ɵɵdomElementEnd()();
|
|
303
|
+
i0.ɵɵdomElementStart(57, "p", 19);
|
|
304
|
+
i0.ɵɵtext(58);
|
|
305
|
+
i0.ɵɵdomElementEnd()()()();
|
|
306
|
+
} if (rf & 2) {
|
|
307
|
+
i0.ɵɵadvance(6);
|
|
308
|
+
i0.ɵɵtextInterpolate(ctx.assessment.targetPacingStatus);
|
|
309
|
+
i0.ɵɵadvance(2);
|
|
310
|
+
i0.ɵɵclassMap(ctx.getStatusBadgeClass());
|
|
311
|
+
i0.ɵɵadvance(4);
|
|
312
|
+
i0.ɵɵtextInterpolate(ctx.getStatusLabel());
|
|
313
|
+
i0.ɵɵadvance();
|
|
314
|
+
i0.ɵɵclassMap(ctx.getGradeBadgeClass());
|
|
315
|
+
i0.ɵɵadvance(4);
|
|
316
|
+
i0.ɵɵtextInterpolate(ctx.assessment.grade);
|
|
317
|
+
i0.ɵɵadvance();
|
|
318
|
+
i0.ɵɵconditional(ctx.revenueMetric ? 18 : -1);
|
|
319
|
+
i0.ɵɵadvance(3);
|
|
320
|
+
i0.ɵɵtextInterpolate(ctx.assessment.narrative);
|
|
321
|
+
i0.ɵɵadvance(10);
|
|
322
|
+
i0.ɵɵtextInterpolate(ctx.assessment.keyStrengths);
|
|
323
|
+
i0.ɵɵadvance(9);
|
|
324
|
+
i0.ɵɵtextInterpolate(ctx.assessment.areasForImprovement);
|
|
325
|
+
i0.ɵɵadvance(9);
|
|
326
|
+
i0.ɵɵtextInterpolate(ctx.assessment.priorYearTrend);
|
|
327
|
+
i0.ɵɵadvance(9);
|
|
328
|
+
i0.ɵɵtextInterpolate(ctx.assessment.recommendedActions);
|
|
329
|
+
} }, encapsulation: 2 }); }
|
|
330
|
+
}
|
|
331
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(OverallAssessmentComponent, [{
|
|
332
|
+
type: Component,
|
|
333
|
+
args: [{
|
|
334
|
+
selector: 'symphiq-funnel-analysis-overall-assessment',
|
|
335
|
+
standalone: true,
|
|
336
|
+
imports: [],
|
|
337
|
+
template: `
|
|
338
|
+
<div class="bg-gradient-to-br from-slate-800 to-slate-900 rounded-2xl p-8 border border-slate-700 shadow-xl">
|
|
339
|
+
<div class="flex items-start justify-between mb-6">
|
|
340
|
+
<div>
|
|
341
|
+
<h2 class="text-3xl font-bold text-white mb-2">Overall Performance</h2>
|
|
342
|
+
<p class="text-slate-400">{{ assessment.targetPacingStatus }}</p>
|
|
343
|
+
</div>
|
|
344
|
+
<div class="flex items-center gap-4">
|
|
345
|
+
<div [class]="getStatusBadgeClass()" class="text-center">
|
|
346
|
+
<div class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1">Status</div>
|
|
347
|
+
<div class="text-lg font-bold">{{ getStatusLabel() }}</div>
|
|
348
|
+
</div>
|
|
349
|
+
<div [class]="getGradeBadgeClass()">
|
|
350
|
+
<div class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1">Grade</div>
|
|
351
|
+
<div class="text-4xl font-bold">{{ assessment.grade }}</div>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
@if (revenueMetric) {
|
|
357
|
+
<div class="bg-gradient-to-r from-emerald-500/20 to-blue-500/20 rounded-xl p-6 mb-6 border-2 border-emerald-500/40">
|
|
358
|
+
<div class="flex items-center justify-between">
|
|
359
|
+
<div class="flex-1">
|
|
360
|
+
<div class="flex items-center gap-3 mb-2">
|
|
361
|
+
<div class="w-12 h-12 bg-emerald-500/30 rounded-lg flex items-center justify-center">
|
|
362
|
+
<svg class="w-7 h-7 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
363
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
364
|
+
</svg>
|
|
365
|
+
</div>
|
|
366
|
+
<div>
|
|
367
|
+
<h3 class="text-sm font-semibold text-slate-400 uppercase tracking-wider">Your Revenue</h3>
|
|
368
|
+
<p class="text-4xl font-bold text-white">{{ formatRevenue(revenueMetric.currentValue || 0) }}</p>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
<div class="flex items-center gap-4 mt-3">
|
|
372
|
+
<div class="flex items-center gap-2">
|
|
373
|
+
<svg class="w-5 h-5 text-emerald-400" fill="currentColor" viewBox="0 0 20 20">
|
|
374
|
+
<path fill-rule="evenodd" d="M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
|
375
|
+
</svg>
|
|
376
|
+
<span class="text-emerald-400 font-semibold text-lg">{{ formatPercent(revenueMetric.trendPercent || 0) }}%</span>
|
|
377
|
+
<span class="text-slate-400 text-sm">vs last year</span>
|
|
378
|
+
</div>
|
|
379
|
+
<div class="h-6 w-px bg-slate-600"></div>
|
|
380
|
+
<div>
|
|
381
|
+
<span class="text-slate-400 text-sm">Target: </span>
|
|
382
|
+
<span class="text-white font-semibold">{{ formatRevenue(revenueMetric.targetValue || 0) }}</span>
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
<div class="text-right">
|
|
387
|
+
<div class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1">Status</div>
|
|
388
|
+
<div [class]="getRevenueStatusClass()" class="inline-block px-4 py-2 rounded-lg font-bold text-lg">
|
|
389
|
+
{{ (revenueMetric.status || 'ON_TRACK').replace('_', ' ') }}
|
|
390
|
+
</div>
|
|
391
|
+
<div class="mt-2 text-sm text-slate-400">{{ revenueMetric.description }}</div>
|
|
392
|
+
</div>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
<div class="bg-slate-900/50 rounded-xl p-6 mb-6 border border-slate-700">
|
|
398
|
+
<p class="text-slate-300 leading-relaxed">{{ assessment.narrative }}</p>
|
|
399
|
+
</div>
|
|
400
|
+
|
|
401
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
402
|
+
<div class="bg-emerald-500/10 rounded-xl p-6 border border-emerald-500/30">
|
|
403
|
+
<div class="flex items-center gap-3 mb-3">
|
|
404
|
+
<div class="w-10 h-10 bg-emerald-500/20 rounded-lg flex items-center justify-center">
|
|
405
|
+
<svg class="w-6 h-6 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
406
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
407
|
+
</svg>
|
|
408
|
+
</div>
|
|
409
|
+
<h3 class="text-lg font-semibold text-emerald-400">Key Strengths</h3>
|
|
410
|
+
</div>
|
|
411
|
+
<p class="text-sm text-slate-300 leading-relaxed">{{ assessment.keyStrengths }}</p>
|
|
412
|
+
</div>
|
|
413
|
+
|
|
414
|
+
<div class="bg-amber-500/10 rounded-xl p-6 border border-amber-500/30">
|
|
415
|
+
<div class="flex items-center gap-3 mb-3">
|
|
416
|
+
<div class="w-10 h-10 bg-amber-500/20 rounded-lg flex items-center justify-center">
|
|
417
|
+
<svg class="w-6 h-6 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
418
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
419
|
+
</svg>
|
|
420
|
+
</div>
|
|
421
|
+
<h3 class="text-lg font-semibold text-amber-400">Areas for Improvement</h3>
|
|
422
|
+
</div>
|
|
423
|
+
<p class="text-sm text-slate-300 leading-relaxed">{{ assessment.areasForImprovement }}</p>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
<div class="bg-blue-500/10 rounded-xl p-6 border border-blue-500/30">
|
|
427
|
+
<div class="flex items-center gap-3 mb-3">
|
|
428
|
+
<div class="w-10 h-10 bg-blue-500/20 rounded-lg flex items-center justify-center">
|
|
429
|
+
<svg class="w-6 h-6 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
430
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
|
|
431
|
+
</svg>
|
|
432
|
+
</div>
|
|
433
|
+
<h3 class="text-lg font-semibold text-blue-400">Prior Year Trend</h3>
|
|
434
|
+
</div>
|
|
435
|
+
<p class="text-sm text-slate-300 leading-relaxed">{{ assessment.priorYearTrend }}</p>
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
<div class="bg-purple-500/10 rounded-xl p-6 border border-purple-500/30">
|
|
439
|
+
<div class="flex items-center gap-3 mb-3">
|
|
440
|
+
<div class="w-10 h-10 bg-purple-500/20 rounded-lg flex items-center justify-center">
|
|
441
|
+
<svg class="w-6 h-6 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
442
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
|
|
443
|
+
</svg>
|
|
444
|
+
</div>
|
|
445
|
+
<h3 class="text-lg font-semibold text-purple-400">Recommended Actions</h3>
|
|
446
|
+
</div>
|
|
447
|
+
<p class="text-sm text-slate-300 leading-relaxed">{{ assessment.recommendedActions }}</p>
|
|
448
|
+
</div>
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
`,
|
|
452
|
+
}]
|
|
453
|
+
}], null, { assessment: [{
|
|
454
|
+
type: Input
|
|
455
|
+
}], revenueMetric: [{
|
|
456
|
+
type: Input
|
|
457
|
+
}] }); })();
|
|
458
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(OverallAssessmentComponent, { className: "OverallAssessmentComponent", filePath: "lib/components/overall-assessment.component.ts", lineNumber: 124 }); })();
|
|
459
|
+
|
|
460
|
+
const _c0$1 = () => [];
|
|
461
|
+
function InsightCardComponent_button_22_Template(rf, ctx) { if (rf & 1) {
|
|
462
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
463
|
+
i0.ɵɵelementStart(0, "button", 10);
|
|
464
|
+
i0.ɵɵlistener("click", function InsightCardComponent_button_22_Template_button_click_0_listener() { const metricName_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.openMetricModal(metricName_r2)); });
|
|
465
|
+
i0.ɵɵtext(1);
|
|
466
|
+
i0.ɵɵelementEnd();
|
|
467
|
+
} if (rf & 2) {
|
|
468
|
+
const metricName_r2 = ctx.$implicit;
|
|
469
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
470
|
+
i0.ɵɵadvance();
|
|
471
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.formatMetricName(metricName_r2), " ");
|
|
472
|
+
} }
|
|
473
|
+
function InsightCardComponent_div_23_li_4_Template(rf, ctx) { if (rf & 1) {
|
|
474
|
+
i0.ɵɵelementStart(0, "li", 14);
|
|
475
|
+
i0.ɵɵnamespaceSVG();
|
|
476
|
+
i0.ɵɵelementStart(1, "svg", 15);
|
|
477
|
+
i0.ɵɵelement(2, "path", 16);
|
|
478
|
+
i0.ɵɵelementEnd();
|
|
479
|
+
i0.ɵɵnamespaceHTML();
|
|
480
|
+
i0.ɵɵelementStart(3, "span", 17);
|
|
481
|
+
i0.ɵɵtext(4);
|
|
482
|
+
i0.ɵɵelementEnd()();
|
|
483
|
+
} if (rf & 2) {
|
|
484
|
+
const rec_r4 = ctx.$implicit;
|
|
485
|
+
i0.ɵɵadvance(4);
|
|
486
|
+
i0.ɵɵtextInterpolate(rec_r4);
|
|
487
|
+
} }
|
|
488
|
+
function InsightCardComponent_div_23_Template(rf, ctx) { if (rf & 1) {
|
|
489
|
+
i0.ɵɵelementStart(0, "div")(1, "h4", 11);
|
|
490
|
+
i0.ɵɵtext(2, "Recommendations");
|
|
491
|
+
i0.ɵɵelementEnd();
|
|
492
|
+
i0.ɵɵelementStart(3, "ul", 12);
|
|
493
|
+
i0.ɵɵtemplate(4, InsightCardComponent_div_23_li_4_Template, 5, 1, "li", 13);
|
|
494
|
+
i0.ɵɵelementEnd()();
|
|
495
|
+
} if (rf & 2) {
|
|
496
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
497
|
+
i0.ɵɵadvance(4);
|
|
498
|
+
i0.ɵɵproperty("ngForOf", ctx_r2.insight.recommendations || i0.ɵɵpureFunction0(1, _c0$1));
|
|
499
|
+
} }
|
|
500
|
+
class InsightCardComponent {
|
|
501
|
+
constructor(modalService) {
|
|
502
|
+
this.modalService = modalService;
|
|
503
|
+
this.allMetrics = [];
|
|
504
|
+
}
|
|
505
|
+
formatMetricName(name) {
|
|
506
|
+
return name.replace(/_/g, ' ').toLowerCase().replace(/\b\w/g, l => l.toUpperCase());
|
|
507
|
+
}
|
|
508
|
+
openMetricModal(metricName) {
|
|
509
|
+
const metric = this.allMetrics.find(m => m.metric === metricName);
|
|
510
|
+
if (metric) {
|
|
511
|
+
this.modalService.openMetricModal(metric);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
getPriorityBadgeClass() {
|
|
515
|
+
const baseClass = 'w-8 h-8 flex items-center justify-center rounded-full text-xs font-bold';
|
|
516
|
+
const priority = this.insight.priority || 0;
|
|
517
|
+
switch (priority) {
|
|
518
|
+
case 1:
|
|
519
|
+
return `${baseClass} bg-red-500/20 text-red-400 border-2 border-red-500/50`;
|
|
520
|
+
case 2:
|
|
521
|
+
return `${baseClass} bg-amber-500/20 text-amber-400 border-2 border-amber-500/50`;
|
|
522
|
+
case 3:
|
|
523
|
+
return `${baseClass} bg-blue-500/20 text-blue-400 border-2 border-blue-500/50`;
|
|
524
|
+
default:
|
|
525
|
+
return `${baseClass} bg-slate-500/20 text-slate-400 border-2 border-slate-500/50`;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
static { this.ɵfac = function InsightCardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || InsightCardComponent)(i0.ɵɵdirectiveInject(ModalService)); }; }
|
|
529
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: InsightCardComponent, selectors: [["symphiq-funnel-analysis-insight-card"]], inputs: { insight: "insight", allMetrics: "allMetrics" }, decls: 24, vars: 8, consts: [[1, "bg-slate-800", "rounded-xl", "p-6", "border", "border-slate-700", "hover:border-slate-600", "transition-all", "duration-300"], [1, "flex", "items-start", "justify-between", "mb-4"], [1, "flex", "items-center", "gap-3"], [1, "text-lg", "font-semibold", "text-white"], [1, "space-y-4"], [1, "text-sm", "font-medium", "text-slate-400", "mb-2"], [1, "text-sm", "text-slate-300", "leading-relaxed"], [1, "flex", "flex-wrap", "gap-2"], ["class", "px-3 py-1 bg-slate-700 hover:bg-slate-600 text-slate-300 hover:text-white text-xs font-medium rounded-full border border-slate-600 hover:border-blue-500 transition-all cursor-pointer", 3, "click", 4, "ngFor", "ngForOf"], [4, "ngIf"], [1, "px-3", "py-1", "bg-slate-700", "hover:bg-slate-600", "text-slate-300", "hover:text-white", "text-xs", "font-medium", "rounded-full", "border", "border-slate-600", "hover:border-blue-500", "transition-all", "cursor-pointer", 3, "click"], [1, "text-sm", "font-medium", "text-slate-400", "mb-3"], [1, "space-y-2"], ["class", "flex items-start gap-2 text-sm text-slate-300", 4, "ngFor", "ngForOf"], [1, "flex", "items-start", "gap-2", "text-sm", "text-slate-300"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-5", "h-5", "text-blue-400", "flex-shrink-0", "mt-0.5"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"], [1, "leading-relaxed"]], template: function InsightCardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
530
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "div");
|
|
531
|
+
i0.ɵɵtext(4);
|
|
532
|
+
i0.ɵɵelementEnd();
|
|
533
|
+
i0.ɵɵelementStart(5, "h3", 3);
|
|
534
|
+
i0.ɵɵtext(6);
|
|
535
|
+
i0.ɵɵelementEnd()()();
|
|
536
|
+
i0.ɵɵelementStart(7, "div", 4)(8, "div")(9, "h4", 5);
|
|
537
|
+
i0.ɵɵtext(10, "Context");
|
|
538
|
+
i0.ɵɵelementEnd();
|
|
539
|
+
i0.ɵɵelementStart(11, "p", 6);
|
|
540
|
+
i0.ɵɵtext(12);
|
|
541
|
+
i0.ɵɵelementEnd()();
|
|
542
|
+
i0.ɵɵelementStart(13, "div")(14, "h4", 5);
|
|
543
|
+
i0.ɵɵtext(15, "Description");
|
|
544
|
+
i0.ɵɵelementEnd();
|
|
545
|
+
i0.ɵɵelementStart(16, "p", 6);
|
|
546
|
+
i0.ɵɵtext(17);
|
|
547
|
+
i0.ɵɵelementEnd()();
|
|
548
|
+
i0.ɵɵelementStart(18, "div")(19, "h4", 5);
|
|
549
|
+
i0.ɵɵtext(20, "Related Metrics");
|
|
550
|
+
i0.ɵɵelementEnd();
|
|
551
|
+
i0.ɵɵelementStart(21, "div", 7);
|
|
552
|
+
i0.ɵɵtemplate(22, InsightCardComponent_button_22_Template, 2, 1, "button", 8);
|
|
553
|
+
i0.ɵɵelementEnd()();
|
|
554
|
+
i0.ɵɵtemplate(23, InsightCardComponent_div_23_Template, 5, 2, "div", 9);
|
|
555
|
+
i0.ɵɵelementEnd()();
|
|
556
|
+
} if (rf & 2) {
|
|
557
|
+
i0.ɵɵadvance(3);
|
|
558
|
+
i0.ɵɵclassMap(ctx.getPriorityBadgeClass());
|
|
559
|
+
i0.ɵɵadvance();
|
|
560
|
+
i0.ɵɵtextInterpolate1(" P", ctx.insight.priority, " ");
|
|
561
|
+
i0.ɵɵadvance(2);
|
|
562
|
+
i0.ɵɵtextInterpolate(ctx.insight.title);
|
|
563
|
+
i0.ɵɵadvance(6);
|
|
564
|
+
i0.ɵɵtextInterpolate(ctx.insight.businessContext);
|
|
565
|
+
i0.ɵɵadvance(5);
|
|
566
|
+
i0.ɵɵtextInterpolate(ctx.insight.description);
|
|
567
|
+
i0.ɵɵadvance(5);
|
|
568
|
+
i0.ɵɵproperty("ngForOf", ctx.insight.relatedMetrics);
|
|
569
|
+
i0.ɵɵadvance();
|
|
570
|
+
i0.ɵɵproperty("ngIf", ((ctx.insight.recommendations == null ? null : ctx.insight.recommendations.length) || 0) > 0);
|
|
571
|
+
} }, dependencies: [CommonModule, i2.NgForOf, i2.NgIf], encapsulation: 2 }); }
|
|
572
|
+
}
|
|
573
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(InsightCardComponent, [{
|
|
574
|
+
type: Component,
|
|
575
|
+
args: [{
|
|
576
|
+
selector: 'symphiq-funnel-analysis-insight-card',
|
|
577
|
+
standalone: true,
|
|
578
|
+
imports: [CommonModule],
|
|
579
|
+
template: `
|
|
580
|
+
<div class="bg-slate-800 rounded-xl p-6 border border-slate-700 hover:border-slate-600 transition-all duration-300">
|
|
581
|
+
<div class="flex items-start justify-between mb-4">
|
|
582
|
+
<div class="flex items-center gap-3">
|
|
583
|
+
<div [class]="getPriorityBadgeClass()">
|
|
584
|
+
P{{ insight.priority }}
|
|
585
|
+
</div>
|
|
586
|
+
<h3 class="text-lg font-semibold text-white">{{ insight.title }}</h3>
|
|
587
|
+
</div>
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
<div class="space-y-4">
|
|
591
|
+
<div>
|
|
592
|
+
<h4 class="text-sm font-medium text-slate-400 mb-2">Context</h4>
|
|
593
|
+
<p class="text-sm text-slate-300 leading-relaxed">{{ insight.businessContext }}</p>
|
|
594
|
+
</div>
|
|
595
|
+
|
|
596
|
+
<div>
|
|
597
|
+
<h4 class="text-sm font-medium text-slate-400 mb-2">Description</h4>
|
|
598
|
+
<p class="text-sm text-slate-300 leading-relaxed">{{ insight.description }}</p>
|
|
599
|
+
</div>
|
|
600
|
+
|
|
601
|
+
<div>
|
|
602
|
+
<h4 class="text-sm font-medium text-slate-400 mb-2">Related Metrics</h4>
|
|
603
|
+
<div class="flex flex-wrap gap-2">
|
|
604
|
+
<button
|
|
605
|
+
*ngFor="let metricName of insight.relatedMetrics"
|
|
606
|
+
(click)="openMetricModal(metricName)"
|
|
607
|
+
class="px-3 py-1 bg-slate-700 hover:bg-slate-600 text-slate-300 hover:text-white text-xs font-medium rounded-full border border-slate-600 hover:border-blue-500 transition-all cursor-pointer">
|
|
608
|
+
{{ formatMetricName(metricName) }}
|
|
609
|
+
</button>
|
|
610
|
+
</div>
|
|
611
|
+
</div>
|
|
612
|
+
|
|
613
|
+
<div *ngIf="(insight.recommendations?.length || 0) > 0">
|
|
614
|
+
<h4 class="text-sm font-medium text-slate-400 mb-3">Recommendations</h4>
|
|
615
|
+
<ul class="space-y-2">
|
|
616
|
+
<li *ngFor="let rec of (insight.recommendations || [])" class="flex items-start gap-2 text-sm text-slate-300">
|
|
617
|
+
<svg class="w-5 h-5 text-blue-400 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
618
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
619
|
+
</svg>
|
|
620
|
+
<span class="leading-relaxed">{{ rec }}</span>
|
|
621
|
+
</li>
|
|
622
|
+
</ul>
|
|
623
|
+
</div>
|
|
624
|
+
</div>
|
|
625
|
+
</div>
|
|
626
|
+
`,
|
|
627
|
+
}]
|
|
628
|
+
}], () => [{ type: ModalService }], { insight: [{
|
|
629
|
+
type: Input
|
|
630
|
+
}], allMetrics: [{
|
|
631
|
+
type: Input
|
|
632
|
+
}] }); })();
|
|
633
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(InsightCardComponent, { className: "InsightCardComponent", filePath: "lib/components/insight-card.component.ts", lineNumber: 59 }); })();
|
|
634
|
+
|
|
635
|
+
function MetricCardComponent_span_6_Template(rf, ctx) { if (rf & 1) {
|
|
636
|
+
i0.ɵɵelementStart(0, "span", 19);
|
|
637
|
+
i0.ɵɵtext(1, " FUNNEL STAGE ");
|
|
638
|
+
i0.ɵɵelementEnd();
|
|
639
|
+
} }
|
|
640
|
+
function MetricCardComponent__svg_svg_11_Template(rf, ctx) { if (rf & 1) {
|
|
641
|
+
i0.ɵɵnamespaceSVG();
|
|
642
|
+
i0.ɵɵelementStart(0, "svg", 20);
|
|
643
|
+
i0.ɵɵelement(1, "path", 21);
|
|
644
|
+
i0.ɵɵelementEnd();
|
|
645
|
+
} }
|
|
646
|
+
function MetricCardComponent__svg_svg_12_Template(rf, ctx) { if (rf & 1) {
|
|
647
|
+
i0.ɵɵnamespaceSVG();
|
|
648
|
+
i0.ɵɵelementStart(0, "svg", 20);
|
|
649
|
+
i0.ɵɵelement(1, "path", 22);
|
|
650
|
+
i0.ɵɵelementEnd();
|
|
651
|
+
} }
|
|
652
|
+
function MetricCardComponent_div_31_button_7_Template(rf, ctx) { if (rf & 1) {
|
|
653
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
654
|
+
i0.ɵɵelementStart(0, "button", 30);
|
|
655
|
+
i0.ɵɵlistener("click", function MetricCardComponent_div_31_button_7_Template_button_click_0_listener() { const insight_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.openInsightModal(insight_r2)); });
|
|
656
|
+
i0.ɵɵelementStart(1, "span", 31);
|
|
657
|
+
i0.ɵɵtext(2);
|
|
658
|
+
i0.ɵɵelementEnd();
|
|
659
|
+
i0.ɵɵelementStart(3, "span", 32);
|
|
660
|
+
i0.ɵɵtext(4);
|
|
661
|
+
i0.ɵɵelementEnd();
|
|
662
|
+
i0.ɵɵnamespaceSVG();
|
|
663
|
+
i0.ɵɵelementStart(5, "svg", 33);
|
|
664
|
+
i0.ɵɵelement(6, "path", 34);
|
|
665
|
+
i0.ɵɵelementEnd()();
|
|
666
|
+
} if (rf & 2) {
|
|
667
|
+
const insight_r2 = ctx.$implicit;
|
|
668
|
+
i0.ɵɵadvance(2);
|
|
669
|
+
i0.ɵɵtextInterpolate(insight_r2.priority);
|
|
670
|
+
i0.ɵɵadvance(2);
|
|
671
|
+
i0.ɵɵtextInterpolate(insight_r2.title);
|
|
672
|
+
} }
|
|
673
|
+
function MetricCardComponent_div_31_Template(rf, ctx) { if (rf & 1) {
|
|
674
|
+
i0.ɵɵelementStart(0, "div", 23)(1, "div", 24);
|
|
675
|
+
i0.ɵɵnamespaceSVG();
|
|
676
|
+
i0.ɵɵelementStart(2, "svg", 25);
|
|
677
|
+
i0.ɵɵelement(3, "path", 26);
|
|
678
|
+
i0.ɵɵelementEnd();
|
|
679
|
+
i0.ɵɵnamespaceHTML();
|
|
680
|
+
i0.ɵɵelementStart(4, "h4", 27);
|
|
681
|
+
i0.ɵɵtext(5, "Related Insights");
|
|
682
|
+
i0.ɵɵelementEnd()();
|
|
683
|
+
i0.ɵɵelementStart(6, "div", 28);
|
|
684
|
+
i0.ɵɵtemplate(7, MetricCardComponent_div_31_button_7_Template, 7, 2, "button", 29);
|
|
685
|
+
i0.ɵɵelementEnd()();
|
|
686
|
+
} if (rf & 2) {
|
|
687
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
688
|
+
i0.ɵɵadvance(7);
|
|
689
|
+
i0.ɵɵproperty("ngForOf", ctx_r2.relatedInsights);
|
|
690
|
+
} }
|
|
691
|
+
class MetricCardComponent {
|
|
692
|
+
constructor(modalService, funnelOrderService) {
|
|
693
|
+
this.modalService = modalService;
|
|
694
|
+
this.funnelOrderService = funnelOrderService;
|
|
695
|
+
this.insights = [];
|
|
696
|
+
this.Math = Math;
|
|
697
|
+
}
|
|
698
|
+
get isFunnelStage() {
|
|
699
|
+
return this.funnelOrderService.isFunnelStage(this.metric.metric || '');
|
|
700
|
+
}
|
|
701
|
+
getCardClass() {
|
|
702
|
+
const baseClass = 'rounded-xl p-6 border transition-all duration-300 flex flex-col h-full';
|
|
703
|
+
if (this.isFunnelStage) {
|
|
704
|
+
return `${baseClass} bg-gradient-to-br from-purple-900/20 to-slate-800 border-purple-700/50 hover:border-purple-600/70`;
|
|
705
|
+
}
|
|
706
|
+
return `${baseClass} bg-slate-800 border-slate-700 hover:border-slate-600`;
|
|
707
|
+
}
|
|
708
|
+
get relatedInsights() {
|
|
709
|
+
if (!this.metric.metric)
|
|
710
|
+
return [];
|
|
711
|
+
return this.insights.filter(insight => insight.relatedMetrics?.includes(this.metric.metric)).sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
712
|
+
}
|
|
713
|
+
openInsightModal(insight) {
|
|
714
|
+
this.modalService.openInsightModal(insight);
|
|
715
|
+
}
|
|
716
|
+
formatMetricName(name) {
|
|
717
|
+
return name.replace(/_/g, ' ').toLowerCase().replace(/\b\w/g, l => l.toUpperCase());
|
|
718
|
+
}
|
|
719
|
+
formatValue(value) {
|
|
720
|
+
if (this.metric.category === 'REVENUE') {
|
|
721
|
+
if (value > 1000) {
|
|
722
|
+
return '$' + value.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
|
723
|
+
}
|
|
724
|
+
return '$' + value.toFixed(2);
|
|
725
|
+
}
|
|
726
|
+
if ((this.metric.metric || '').includes('RATE') || (this.metric.metric || '').includes('CONVERSION')) {
|
|
727
|
+
return value.toFixed(2) + '%';
|
|
728
|
+
}
|
|
729
|
+
if (value < 100 && value % 1 !== 0) {
|
|
730
|
+
return value.toFixed(2);
|
|
731
|
+
}
|
|
732
|
+
return value.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
|
733
|
+
}
|
|
734
|
+
getTrendClass() {
|
|
735
|
+
const isGoodTrend = this.isGoodTrend();
|
|
736
|
+
if (isGoodTrend) {
|
|
737
|
+
return 'text-emerald-400';
|
|
738
|
+
}
|
|
739
|
+
return 'text-red-400';
|
|
740
|
+
}
|
|
741
|
+
isGoodTrend() {
|
|
742
|
+
if (this.metric.metric === 'BOUNCE_RATE') {
|
|
743
|
+
return (this.metric.trendDirection || '') === 'DOWN';
|
|
744
|
+
}
|
|
745
|
+
return (this.metric.trendDirection || '') === 'UP';
|
|
746
|
+
}
|
|
747
|
+
getStatusBadgeClass() {
|
|
748
|
+
const baseClass = 'px-3 py-1 rounded-full text-xs font-semibold whitespace-nowrap';
|
|
749
|
+
switch (this.metric.status || 'ON_TRACK') {
|
|
750
|
+
case 'OVERACHIEVING':
|
|
751
|
+
return `${baseClass} bg-emerald-500/20 text-emerald-400 border border-emerald-500/30`;
|
|
752
|
+
case 'ON_TRACK':
|
|
753
|
+
return `${baseClass} bg-blue-500/20 text-blue-400 border border-blue-500/30`;
|
|
754
|
+
case 'AT_RISK':
|
|
755
|
+
return `${baseClass} bg-amber-500/20 text-amber-400 border border-amber-500/30`;
|
|
756
|
+
default:
|
|
757
|
+
return `${baseClass} bg-slate-500/20 text-slate-400 border border-slate-500/30`;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
getStatusLabel() {
|
|
761
|
+
return (this.metric.status || 'ON_TRACK').replace(/_/g, ' ');
|
|
762
|
+
}
|
|
763
|
+
getPacingPercentage() {
|
|
764
|
+
const currentValue = this.metric.currentValue || 0;
|
|
765
|
+
const targetValue = this.metric.targetValue || 1;
|
|
766
|
+
const percentage = ((currentValue / targetValue) * 100);
|
|
767
|
+
return Math.min(percentage, 100);
|
|
768
|
+
}
|
|
769
|
+
getPacingBarClass() {
|
|
770
|
+
if ((this.metric.status || '') === 'OVERACHIEVING') {
|
|
771
|
+
return 'bg-gradient-to-r from-emerald-500 to-emerald-400';
|
|
772
|
+
}
|
|
773
|
+
if ((this.metric.status || '') === 'AT_RISK') {
|
|
774
|
+
return 'bg-gradient-to-r from-amber-500 to-amber-400';
|
|
775
|
+
}
|
|
776
|
+
return 'bg-gradient-to-r from-blue-500 to-blue-400';
|
|
777
|
+
}
|
|
778
|
+
static { this.ɵfac = function MetricCardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MetricCardComponent)(i0.ɵɵdirectiveInject(ModalService), i0.ɵɵdirectiveInject(FunnelOrderService)); }; }
|
|
779
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MetricCardComponent, selectors: [["symphiq-funnel-analysis-metric-card"]], inputs: { metric: "metric", insights: "insights" }, decls: 32, vars: 23, consts: [[1, "flex", "items-start", "justify-between", "mb-4"], [1, "flex-1"], [1, "flex", "items-center", "gap-2", "mb-1"], [1, "text-sm", "font-medium", "text-slate-400"], ["class", "px-2 py-0.5 bg-purple-500/20 text-purple-300 text-xs font-semibold rounded border border-purple-500/30", 4, "ngIf"], [1, "flex", "items-baseline", "gap-2"], [1, "text-3xl", "font-bold", "text-white"], [1, "text-sm", "font-semibold", "flex", "items-center", "gap-1"], ["class", "w-4 h-4", "fill", "currentColor", "viewBox", "0 0 20 20", 4, "ngIf"], [1, "space-y-3"], [1, "flex", "justify-between", "items-center", "text-sm"], [1, "text-slate-400"], [1, "text-white", "font-medium"], [1, "w-full", "bg-slate-700", "rounded-full", "h-2.5", "overflow-hidden"], [1, "h-2.5", "rounded-full", "transition-all", "duration-500"], [1, "flex", "justify-between", "items-center", "text-xs"], [1, "text-slate-500"], [1, "mt-4", "text-sm", "text-slate-400", "leading-relaxed", "flex-grow"], ["class", "mt-6 flex-shrink-0", 4, "ngIf"], [1, "px-2", "py-0.5", "bg-purple-500/20", "text-purple-300", "text-xs", "font-semibold", "rounded", "border", "border-purple-500/30"], ["fill", "currentColor", "viewBox", "0 0 20 20", 1, "w-4", "h-4"], ["fill-rule", "evenodd", "d", "M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z", "clip-rule", "evenodd"], ["fill-rule", "evenodd", "d", "M14.707 10.293a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L9 12.586V5a1 1 0 012 0v7.586l2.293-2.293a1 1 0 011.414 0z", "clip-rule", "evenodd"], [1, "mt-6", "flex-shrink-0"], [1, "flex", "items-center", "gap-2", "mb-3"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-4", "h-4", "text-blue-400"], ["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"], [1, "text-xs", "font-semibold", "text-slate-300", "uppercase", "tracking-wide"], [1, "space-y-2"], ["class", "w-full text-left flex items-center gap-2 p-3 bg-slate-900/30 hover:bg-slate-900/50 rounded-lg border border-slate-700/50 hover:border-blue-500/50 transition-all cursor-pointer group", 3, "click", 4, "ngFor", "ngForOf"], [1, "w-full", "text-left", "flex", "items-center", "gap-2", "p-3", "bg-slate-900/30", "hover:bg-slate-900/50", "rounded-lg", "border", "border-slate-700/50", "hover:border-blue-500/50", "transition-all", "cursor-pointer", "group", 3, "click"], [1, "inline-flex", "items-center", "justify-center", "w-6", "h-6", "rounded-full", "bg-blue-500/20", "text-blue-400", "text-xs", "font-bold", "flex-shrink-0", "group-hover:bg-blue-500/30"], [1, "text-sm", "text-slate-300", "group-hover:text-white", "transition-colors", "flex-1"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-4", "h-4", "text-slate-500", "group-hover:text-blue-400", "transition-colors"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M9 5l7 7-7 7"]], template: function MetricCardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
780
|
+
i0.ɵɵelementStart(0, "div")(1, "div", 0)(2, "div", 1)(3, "div", 2)(4, "h3", 3);
|
|
781
|
+
i0.ɵɵtext(5);
|
|
782
|
+
i0.ɵɵelementEnd();
|
|
783
|
+
i0.ɵɵtemplate(6, MetricCardComponent_span_6_Template, 2, 0, "span", 4);
|
|
784
|
+
i0.ɵɵelementEnd();
|
|
785
|
+
i0.ɵɵelementStart(7, "div", 5)(8, "span", 6);
|
|
786
|
+
i0.ɵɵtext(9);
|
|
787
|
+
i0.ɵɵelementEnd();
|
|
788
|
+
i0.ɵɵelementStart(10, "span", 7);
|
|
789
|
+
i0.ɵɵtemplate(11, MetricCardComponent__svg_svg_11_Template, 2, 0, "svg", 8)(12, MetricCardComponent__svg_svg_12_Template, 2, 0, "svg", 8);
|
|
790
|
+
i0.ɵɵtext(13);
|
|
791
|
+
i0.ɵɵelementEnd()()();
|
|
792
|
+
i0.ɵɵelementStart(14, "div");
|
|
793
|
+
i0.ɵɵtext(15);
|
|
794
|
+
i0.ɵɵelementEnd()();
|
|
795
|
+
i0.ɵɵelementStart(16, "div", 9)(17, "div", 10)(18, "span", 11);
|
|
796
|
+
i0.ɵɵtext(19, "Target");
|
|
797
|
+
i0.ɵɵelementEnd();
|
|
798
|
+
i0.ɵɵelementStart(20, "span", 12);
|
|
799
|
+
i0.ɵɵtext(21);
|
|
800
|
+
i0.ɵɵelementEnd()();
|
|
801
|
+
i0.ɵɵelementStart(22, "div", 13);
|
|
802
|
+
i0.ɵɵelement(23, "div", 14);
|
|
803
|
+
i0.ɵɵelementEnd();
|
|
804
|
+
i0.ɵɵelementStart(24, "div", 15)(25, "span", 16);
|
|
805
|
+
i0.ɵɵtext(26);
|
|
806
|
+
i0.ɵɵelementEnd();
|
|
807
|
+
i0.ɵɵelementStart(27, "span", 16);
|
|
808
|
+
i0.ɵɵtext(28);
|
|
809
|
+
i0.ɵɵelementEnd()()();
|
|
810
|
+
i0.ɵɵelementStart(29, "p", 17);
|
|
811
|
+
i0.ɵɵtext(30);
|
|
812
|
+
i0.ɵɵelementEnd();
|
|
813
|
+
i0.ɵɵtemplate(31, MetricCardComponent_div_31_Template, 8, 1, "div", 18);
|
|
814
|
+
i0.ɵɵelementEnd();
|
|
815
|
+
} if (rf & 2) {
|
|
816
|
+
i0.ɵɵclassMap(ctx.getCardClass());
|
|
817
|
+
i0.ɵɵadvance(5);
|
|
818
|
+
i0.ɵɵtextInterpolate(ctx.formatMetricName(ctx.metric.metric || "UNKNOWN"));
|
|
819
|
+
i0.ɵɵadvance();
|
|
820
|
+
i0.ɵɵproperty("ngIf", ctx.isFunnelStage);
|
|
821
|
+
i0.ɵɵadvance(3);
|
|
822
|
+
i0.ɵɵtextInterpolate(ctx.formatValue(ctx.metric.currentValue || 0));
|
|
823
|
+
i0.ɵɵadvance();
|
|
824
|
+
i0.ɵɵclassMap(ctx.getTrendClass());
|
|
825
|
+
i0.ɵɵadvance();
|
|
826
|
+
i0.ɵɵproperty("ngIf", ctx.metric.trendDirection === "UP");
|
|
827
|
+
i0.ɵɵadvance();
|
|
828
|
+
i0.ɵɵproperty("ngIf", ctx.metric.trendDirection === "DOWN");
|
|
829
|
+
i0.ɵɵadvance();
|
|
830
|
+
i0.ɵɵtextInterpolate1(" ", ctx.Math.abs(ctx.metric.trendPercent || 0).toFixed(1), "% ");
|
|
831
|
+
i0.ɵɵadvance();
|
|
832
|
+
i0.ɵɵclassMap(ctx.getStatusBadgeClass());
|
|
833
|
+
i0.ɵɵadvance();
|
|
834
|
+
i0.ɵɵtextInterpolate1(" ", ctx.getStatusLabel(), " ");
|
|
835
|
+
i0.ɵɵadvance(6);
|
|
836
|
+
i0.ɵɵtextInterpolate(ctx.formatValue(ctx.metric.targetValue || 0));
|
|
837
|
+
i0.ɵɵadvance(2);
|
|
838
|
+
i0.ɵɵclassMap(ctx.getPacingBarClass());
|
|
839
|
+
i0.ɵɵstyleProp("width", ctx.getPacingPercentage(), "%");
|
|
840
|
+
i0.ɵɵadvance(3);
|
|
841
|
+
i0.ɵɵtextInterpolate2("Pacing: ", (ctx.metric.pacingPercentage || 0) > 0 ? "+" : "", "", (ctx.metric.pacingPercentage || 0).toFixed(1), "%");
|
|
842
|
+
i0.ɵɵadvance(2);
|
|
843
|
+
i0.ɵɵtextInterpolate1("Priority: ", ctx.metric.priority || 0);
|
|
844
|
+
i0.ɵɵadvance(2);
|
|
845
|
+
i0.ɵɵtextInterpolate(ctx.metric.description);
|
|
846
|
+
i0.ɵɵadvance();
|
|
847
|
+
i0.ɵɵproperty("ngIf", ctx.relatedInsights.length > 0);
|
|
848
|
+
} }, dependencies: [CommonModule, i2.NgForOf, i2.NgIf], encapsulation: 2 }); }
|
|
849
|
+
}
|
|
850
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MetricCardComponent, [{
|
|
851
|
+
type: Component,
|
|
852
|
+
args: [{
|
|
853
|
+
selector: 'symphiq-funnel-analysis-metric-card',
|
|
854
|
+
standalone: true,
|
|
855
|
+
imports: [CommonModule],
|
|
856
|
+
template: `
|
|
857
|
+
<div [class]="getCardClass()">
|
|
858
|
+
<div class="flex items-start justify-between mb-4">
|
|
859
|
+
<div class="flex-1">
|
|
860
|
+
<div class="flex items-center gap-2 mb-1">
|
|
861
|
+
<h3 class="text-sm font-medium text-slate-400">{{ formatMetricName(metric.metric || 'UNKNOWN') }}</h3>
|
|
862
|
+
<span *ngIf="isFunnelStage" class="px-2 py-0.5 bg-purple-500/20 text-purple-300 text-xs font-semibold rounded border border-purple-500/30">
|
|
863
|
+
FUNNEL STAGE
|
|
864
|
+
</span>
|
|
865
|
+
</div>
|
|
866
|
+
<div class="flex items-baseline gap-2">
|
|
867
|
+
<span class="text-3xl font-bold text-white">{{ formatValue(metric.currentValue || 0) }}</span>
|
|
868
|
+
<span [class]="getTrendClass()" class="text-sm font-semibold flex items-center gap-1">
|
|
869
|
+
<svg *ngIf="metric.trendDirection === 'UP'" class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
|
870
|
+
<path fill-rule="evenodd" d="M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
|
871
|
+
</svg>
|
|
872
|
+
<svg *ngIf="metric.trendDirection === 'DOWN'" class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
|
873
|
+
<path fill-rule="evenodd" d="M14.707 10.293a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L9 12.586V5a1 1 0 012 0v7.586l2.293-2.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
|
874
|
+
</svg>
|
|
875
|
+
{{ Math.abs(metric.trendPercent || 0).toFixed(1) }}%
|
|
876
|
+
</span>
|
|
877
|
+
</div>
|
|
878
|
+
</div>
|
|
879
|
+
<div [class]="getStatusBadgeClass()">
|
|
880
|
+
{{ getStatusLabel() }}
|
|
881
|
+
</div>
|
|
882
|
+
</div>
|
|
883
|
+
|
|
884
|
+
<div class="space-y-3">
|
|
885
|
+
<div class="flex justify-between items-center text-sm">
|
|
886
|
+
<span class="text-slate-400">Target</span>
|
|
887
|
+
<span class="text-white font-medium">{{ formatValue(metric.targetValue || 0) }}</span>
|
|
888
|
+
</div>
|
|
889
|
+
|
|
890
|
+
<div class="w-full bg-slate-700 rounded-full h-2.5 overflow-hidden">
|
|
891
|
+
<div
|
|
892
|
+
[style.width.%]="getPacingPercentage()"
|
|
893
|
+
[class]="getPacingBarClass()"
|
|
894
|
+
class="h-2.5 rounded-full transition-all duration-500">
|
|
895
|
+
</div>
|
|
896
|
+
</div>
|
|
897
|
+
|
|
898
|
+
<div class="flex justify-between items-center text-xs">
|
|
899
|
+
<span class="text-slate-500">Pacing: {{ (metric.pacingPercentage || 0) > 0 ? '+' : '' }}{{ (metric.pacingPercentage || 0).toFixed(1) }}%</span>
|
|
900
|
+
<span class="text-slate-500">Priority: {{ metric.priority || 0 }}</span>
|
|
901
|
+
</div>
|
|
902
|
+
</div>
|
|
903
|
+
|
|
904
|
+
<p class="mt-4 text-sm text-slate-400 leading-relaxed flex-grow">{{ metric.description }}</p>
|
|
905
|
+
|
|
906
|
+
<div *ngIf="relatedInsights.length > 0" class="mt-6 flex-shrink-0">
|
|
907
|
+
<div class="flex items-center gap-2 mb-3">
|
|
908
|
+
<svg class="w-4 h-4 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
909
|
+
<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"></path>
|
|
910
|
+
</svg>
|
|
911
|
+
<h4 class="text-xs font-semibold text-slate-300 uppercase tracking-wide">Related Insights</h4>
|
|
912
|
+
</div>
|
|
913
|
+
<div class="space-y-2">
|
|
914
|
+
<button
|
|
915
|
+
*ngFor="let insight of relatedInsights"
|
|
916
|
+
(click)="openInsightModal(insight)"
|
|
917
|
+
class="w-full text-left flex items-center gap-2 p-3 bg-slate-900/30 hover:bg-slate-900/50 rounded-lg border border-slate-700/50 hover:border-blue-500/50 transition-all cursor-pointer group">
|
|
918
|
+
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-500/20 text-blue-400 text-xs font-bold flex-shrink-0 group-hover:bg-blue-500/30">{{ insight.priority }}</span>
|
|
919
|
+
<span class="text-sm text-slate-300 group-hover:text-white transition-colors flex-1">{{ insight.title }}</span>
|
|
920
|
+
<svg class="w-4 h-4 text-slate-500 group-hover:text-blue-400 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
921
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
922
|
+
</svg>
|
|
923
|
+
</button>
|
|
924
|
+
</div>
|
|
925
|
+
</div>
|
|
926
|
+
</div>
|
|
927
|
+
`,
|
|
928
|
+
}]
|
|
929
|
+
}], () => [{ type: ModalService }, { type: FunnelOrderService }], { metric: [{
|
|
930
|
+
type: Input
|
|
931
|
+
}], insights: [{
|
|
932
|
+
type: Input
|
|
933
|
+
}] }); })();
|
|
934
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MetricCardComponent, { className: "MetricCardComponent", filePath: "lib/components/metric-card.component.ts", lineNumber: 84 }); })();
|
|
935
|
+
|
|
936
|
+
function BreakdownSectionComponent_div_7_ng_container_18__svg_svg_13_Template(rf, ctx) { if (rf & 1) {
|
|
937
|
+
i0.ɵɵnamespaceSVG();
|
|
938
|
+
i0.ɵɵelementStart(0, "svg", 28);
|
|
939
|
+
i0.ɵɵelement(1, "path", 29);
|
|
940
|
+
i0.ɵɵelementEnd();
|
|
941
|
+
} }
|
|
942
|
+
function BreakdownSectionComponent_div_7_ng_container_18__svg_svg_14_Template(rf, ctx) { if (rf & 1) {
|
|
943
|
+
i0.ɵɵnamespaceSVG();
|
|
944
|
+
i0.ɵɵelementStart(0, "svg", 28);
|
|
945
|
+
i0.ɵɵelement(1, "path", 30);
|
|
946
|
+
i0.ɵɵelementEnd();
|
|
947
|
+
} }
|
|
948
|
+
function BreakdownSectionComponent_div_7_ng_container_18_Template(rf, ctx) { if (rf & 1) {
|
|
949
|
+
i0.ɵɵelementContainerStart(0);
|
|
950
|
+
i0.ɵɵelementStart(1, "tr", 15)(2, "td", 16)(3, "div")(4, "div", 17);
|
|
951
|
+
i0.ɵɵtext(5);
|
|
952
|
+
i0.ɵɵelementEnd();
|
|
953
|
+
i0.ɵɵelementStart(6, "div", 18);
|
|
954
|
+
i0.ɵɵtext(7);
|
|
955
|
+
i0.ɵɵelementEnd()()();
|
|
956
|
+
i0.ɵɵelementStart(8, "td", 19)(9, "div", 20);
|
|
957
|
+
i0.ɵɵtext(10);
|
|
958
|
+
i0.ɵɵelementEnd()();
|
|
959
|
+
i0.ɵɵelementStart(11, "td", 19)(12, "div", 21);
|
|
960
|
+
i0.ɵɵtemplate(13, BreakdownSectionComponent_div_7_ng_container_18__svg_svg_13_Template, 2, 0, "svg", 22)(14, BreakdownSectionComponent_div_7_ng_container_18__svg_svg_14_Template, 2, 0, "svg", 22);
|
|
961
|
+
i0.ɵɵtext(15);
|
|
962
|
+
i0.ɵɵelementEnd()();
|
|
963
|
+
i0.ɵɵelementStart(16, "td", 19)(17, "span", 23);
|
|
964
|
+
i0.ɵɵtext(18);
|
|
965
|
+
i0.ɵɵelementEnd()()();
|
|
966
|
+
i0.ɵɵelementStart(19, "tr", 24)(20, "td", 25)(21, "div", 26)(22, "p", 27);
|
|
967
|
+
i0.ɵɵtext(23);
|
|
968
|
+
i0.ɵɵelementEnd()()()();
|
|
969
|
+
i0.ɵɵelementContainerEnd();
|
|
970
|
+
} if (rf & 2) {
|
|
971
|
+
const metric_r1 = ctx.$implicit;
|
|
972
|
+
const i_r2 = ctx.index;
|
|
973
|
+
const group_r3 = i0.ɵɵnextContext().$implicit;
|
|
974
|
+
const ctx_r3 = i0.ɵɵnextContext();
|
|
975
|
+
i0.ɵɵadvance(5);
|
|
976
|
+
i0.ɵɵtextInterpolate(ctx_r3.formatDimensionValue(metric_r1.dimensionValue));
|
|
977
|
+
i0.ɵɵadvance(2);
|
|
978
|
+
i0.ɵɵtextInterpolate1("Priority ", metric_r1.priority);
|
|
979
|
+
i0.ɵɵadvance(3);
|
|
980
|
+
i0.ɵɵtextInterpolate(ctx_r3.formatValue(metric_r1.currentValue || 0, metric_r1.category, metric_r1.metric));
|
|
981
|
+
i0.ɵɵadvance(2);
|
|
982
|
+
i0.ɵɵclassMap(ctx_r3.getTrendClass(metric_r1));
|
|
983
|
+
i0.ɵɵadvance();
|
|
984
|
+
i0.ɵɵproperty("ngIf", metric_r1.trendDirection === "UP");
|
|
985
|
+
i0.ɵɵadvance();
|
|
986
|
+
i0.ɵɵproperty("ngIf", metric_r1.trendDirection === "DOWN");
|
|
987
|
+
i0.ɵɵadvance();
|
|
988
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r3.Math.abs(metric_r1.trendPercent || 0).toFixed(1), "% ");
|
|
989
|
+
i0.ɵɵadvance(2);
|
|
990
|
+
i0.ɵɵclassMap(ctx_r3.getStatusBadgeClass(metric_r1.status || "ON_TRACK"));
|
|
991
|
+
i0.ɵɵadvance();
|
|
992
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r3.getStatusLabel(metric_r1.status || "ON_TRACK"), " ");
|
|
993
|
+
i0.ɵɵadvance();
|
|
994
|
+
i0.ɵɵclassProp("border-b", i_r2 < group_r3.values.length - 1);
|
|
995
|
+
i0.ɵɵadvance(4);
|
|
996
|
+
i0.ɵɵtextInterpolate(metric_r1.description);
|
|
997
|
+
} }
|
|
998
|
+
function BreakdownSectionComponent_div_7_Template(rf, ctx) { if (rf & 1) {
|
|
999
|
+
i0.ɵɵelementStart(0, "div", 6)(1, "h4", 7);
|
|
1000
|
+
i0.ɵɵtext(2);
|
|
1001
|
+
i0.ɵɵelementStart(3, "span", 8);
|
|
1002
|
+
i0.ɵɵtext(4);
|
|
1003
|
+
i0.ɵɵelementEnd()();
|
|
1004
|
+
i0.ɵɵelementStart(5, "div", 9)(6, "table", 10)(7, "thead")(8, "tr", 11)(9, "th", 12);
|
|
1005
|
+
i0.ɵɵtext(10);
|
|
1006
|
+
i0.ɵɵelementEnd();
|
|
1007
|
+
i0.ɵɵelementStart(11, "th", 13);
|
|
1008
|
+
i0.ɵɵtext(12, "Current");
|
|
1009
|
+
i0.ɵɵelementEnd();
|
|
1010
|
+
i0.ɵɵelementStart(13, "th", 13);
|
|
1011
|
+
i0.ɵɵtext(14, "Trend");
|
|
1012
|
+
i0.ɵɵelementEnd();
|
|
1013
|
+
i0.ɵɵelementStart(15, "th", 13);
|
|
1014
|
+
i0.ɵɵtext(16, "Status");
|
|
1015
|
+
i0.ɵɵelementEnd()()();
|
|
1016
|
+
i0.ɵɵelementStart(17, "tbody");
|
|
1017
|
+
i0.ɵɵtemplate(18, BreakdownSectionComponent_div_7_ng_container_18_Template, 24, 14, "ng-container", 14);
|
|
1018
|
+
i0.ɵɵelementEnd()()()();
|
|
1019
|
+
} if (rf & 2) {
|
|
1020
|
+
const group_r3 = ctx.$implicit;
|
|
1021
|
+
const ctx_r3 = i0.ɵɵnextContext();
|
|
1022
|
+
i0.ɵɵadvance(2);
|
|
1023
|
+
i0.ɵɵtextInterpolate1(" ", group_r3.metricLabel, " ");
|
|
1024
|
+
i0.ɵɵadvance(2);
|
|
1025
|
+
i0.ɵɵtextInterpolate(group_r3.category);
|
|
1026
|
+
i0.ɵɵadvance(6);
|
|
1027
|
+
i0.ɵɵtextInterpolate(ctx_r3.breakdown.dimensionLabel);
|
|
1028
|
+
i0.ɵɵadvance(8);
|
|
1029
|
+
i0.ɵɵproperty("ngForOf", group_r3.values);
|
|
1030
|
+
} }
|
|
1031
|
+
class BreakdownSectionComponent {
|
|
1032
|
+
constructor() {
|
|
1033
|
+
this.Math = Math;
|
|
1034
|
+
this.metricGroups = [];
|
|
1035
|
+
}
|
|
1036
|
+
ngOnInit() {
|
|
1037
|
+
this.metricGroups = this.groupMetrics();
|
|
1038
|
+
}
|
|
1039
|
+
groupMetrics() {
|
|
1040
|
+
const grouped = new Map();
|
|
1041
|
+
if (!this.breakdown.subMetrics) {
|
|
1042
|
+
return [];
|
|
1043
|
+
}
|
|
1044
|
+
for (const metric of this.breakdown.subMetrics) {
|
|
1045
|
+
if (!metric.metric)
|
|
1046
|
+
continue;
|
|
1047
|
+
if (!grouped.has(metric.metric)) {
|
|
1048
|
+
grouped.set(metric.metric, {
|
|
1049
|
+
metric: metric.metric,
|
|
1050
|
+
metricLabel: this.formatMetricLabel(metric.metric),
|
|
1051
|
+
category: metric.category || '',
|
|
1052
|
+
values: []
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
grouped.get(metric.metric).values.push(metric);
|
|
1056
|
+
}
|
|
1057
|
+
return Array.from(grouped.values());
|
|
1058
|
+
}
|
|
1059
|
+
formatMetricLabel(metric) {
|
|
1060
|
+
return metric
|
|
1061
|
+
.split('_')
|
|
1062
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
1063
|
+
.join(' ');
|
|
1064
|
+
}
|
|
1065
|
+
formatDimensionValue(value) {
|
|
1066
|
+
if (!value || value === '(none)')
|
|
1067
|
+
return 'Direct';
|
|
1068
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
1069
|
+
}
|
|
1070
|
+
formatValue(value, category, metric) {
|
|
1071
|
+
if ((category || '') === 'REVENUE') {
|
|
1072
|
+
return '$' + value.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
|
1073
|
+
}
|
|
1074
|
+
if (metric?.includes('RATE') || metric?.includes('CONVERSION')) {
|
|
1075
|
+
if (value < 1) {
|
|
1076
|
+
return (value * 100).toFixed(2) + '%';
|
|
1077
|
+
}
|
|
1078
|
+
return value.toFixed(2) + '%';
|
|
1079
|
+
}
|
|
1080
|
+
return value.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
|
1081
|
+
}
|
|
1082
|
+
getTrendClass(metric) {
|
|
1083
|
+
return metric.trendDirection === 'UP' ? 'text-emerald-400' : 'text-red-400';
|
|
1084
|
+
}
|
|
1085
|
+
getStatusBadgeClass(status) {
|
|
1086
|
+
const baseClass = 'px-2 py-1 rounded-full text-xs font-semibold';
|
|
1087
|
+
switch (status || 'ON_TRACK') {
|
|
1088
|
+
case 'OVERACHIEVING':
|
|
1089
|
+
return `${baseClass} bg-emerald-500/20 text-emerald-400`;
|
|
1090
|
+
case 'ON_TRACK':
|
|
1091
|
+
return `${baseClass} bg-blue-500/20 text-blue-400`;
|
|
1092
|
+
case 'AT_RISK':
|
|
1093
|
+
return `${baseClass} bg-amber-500/20 text-amber-400`;
|
|
1094
|
+
default:
|
|
1095
|
+
return `${baseClass} bg-slate-500/20 text-slate-400`;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
getStatusLabel(status) {
|
|
1099
|
+
return (status || '').replace(/_/g, ' ');
|
|
1100
|
+
}
|
|
1101
|
+
static { this.ɵfac = function BreakdownSectionComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || BreakdownSectionComponent)(); }; }
|
|
1102
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: BreakdownSectionComponent, selectors: [["symphiq-funnel-analysis-breakdown-section"]], inputs: { breakdown: "breakdown" }, decls: 8, vars: 3, consts: [[1, "bg-slate-800", "rounded-xl", "p-6", "border", "border-slate-700", 2, "margin-bottom", "2rem"], [1, "mb-6"], [1, "text-xl", "font-bold", "text-white", "mb-2"], [1, "text-sm", "text-slate-400", "leading-relaxed"], [1, "space-y-8"], ["class", "border-t border-slate-700 pt-6 first:border-t-0 first:pt-0", 4, "ngFor", "ngForOf"], [1, "border-t", "border-slate-700", "pt-6", "first:border-t-0", "first:pt-0"], [1, "text-base", "font-semibold", "text-white", "mb-4", "flex", "items-center", "gap-2"], [1, "text-xs", "font-normal", "text-slate-500", "px-2", "py-1", "bg-slate-700/50", "rounded"], [1, "overflow-x-auto"], [1, "w-full"], [1, "border-b", "border-slate-700"], [1, "text-left", "py-3", "px-4", "text-xs", "font-semibold", "text-slate-400", "uppercase", "tracking-wider"], [1, "text-right", "py-3", "px-4", "text-xs", "font-semibold", "text-slate-400", "uppercase", "tracking-wider"], [4, "ngFor", "ngForOf"], [1, "hover:bg-slate-700/30", "transition-colors"], [1, "py-3", "px-4", "pb-1"], [1, "text-sm", "font-medium", "text-white"], [1, "text-xs", "text-slate-500", "mt-1"], [1, "text-right", "py-3", "px-4", "pb-1"], [1, "text-sm", "font-semibold", "text-white"], [1, "text-sm", "font-semibold", "flex", "items-center", "justify-end", "gap-1"], ["class", "w-4 h-4", "fill", "currentColor", "viewBox", "0 0 20 20", 4, "ngIf"], [1, "inline-block"], [1, "border-slate-700"], ["colspan", "4", 1, "px-4", "pt-0", "pb-4"], [1, "bg-slate-900/50", "rounded-lg", "p-3", "border", "border-slate-700/50"], [1, "text-xs", "text-slate-400", "leading-relaxed"], ["fill", "currentColor", "viewBox", "0 0 20 20", 1, "w-4", "h-4"], ["fill-rule", "evenodd", "d", "M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z", "clip-rule", "evenodd"], ["fill-rule", "evenodd", "d", "M14.707 10.293a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L9 12.586V5a1 1 0 012 0v7.586l2.293-2.293a1 1 0 011.414 0z", "clip-rule", "evenodd"]], template: function BreakdownSectionComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1103
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "h3", 2);
|
|
1104
|
+
i0.ɵɵtext(3);
|
|
1105
|
+
i0.ɵɵelementEnd();
|
|
1106
|
+
i0.ɵɵelementStart(4, "p", 3);
|
|
1107
|
+
i0.ɵɵtext(5);
|
|
1108
|
+
i0.ɵɵelementEnd()();
|
|
1109
|
+
i0.ɵɵelementStart(6, "div", 4);
|
|
1110
|
+
i0.ɵɵtemplate(7, BreakdownSectionComponent_div_7_Template, 19, 4, "div", 5);
|
|
1111
|
+
i0.ɵɵelementEnd()();
|
|
1112
|
+
} if (rf & 2) {
|
|
1113
|
+
i0.ɵɵadvance(3);
|
|
1114
|
+
i0.ɵɵtextInterpolate1("", ctx.breakdown.dimensionLabel, " Analysis");
|
|
1115
|
+
i0.ɵɵadvance(2);
|
|
1116
|
+
i0.ɵɵtextInterpolate(ctx.breakdown.summaryAnalysis);
|
|
1117
|
+
i0.ɵɵadvance(2);
|
|
1118
|
+
i0.ɵɵproperty("ngForOf", ctx.metricGroups);
|
|
1119
|
+
} }, dependencies: [CommonModule, i2.NgForOf, i2.NgIf], encapsulation: 2 }); }
|
|
1120
|
+
}
|
|
1121
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(BreakdownSectionComponent, [{
|
|
1122
|
+
type: Component,
|
|
1123
|
+
args: [{
|
|
1124
|
+
selector: 'symphiq-funnel-analysis-breakdown-section',
|
|
1125
|
+
standalone: true,
|
|
1126
|
+
imports: [CommonModule],
|
|
1127
|
+
template: `
|
|
1128
|
+
<div class="bg-slate-800 rounded-xl p-6 border border-slate-700" style="margin-bottom: 2rem;">
|
|
1129
|
+
<div class="mb-6">
|
|
1130
|
+
<h3 class="text-xl font-bold text-white mb-2">{{ breakdown.dimensionLabel }} Analysis</h3>
|
|
1131
|
+
<p class="text-sm text-slate-400 leading-relaxed">{{ breakdown.summaryAnalysis }}</p>
|
|
1132
|
+
</div>
|
|
1133
|
+
|
|
1134
|
+
<div class="space-y-8">
|
|
1135
|
+
<div *ngFor="let group of metricGroups" class="border-t border-slate-700 pt-6 first:border-t-0 first:pt-0">
|
|
1136
|
+
<h4 class="text-base font-semibold text-white mb-4 flex items-center gap-2">
|
|
1137
|
+
{{ group.metricLabel }}
|
|
1138
|
+
<span class="text-xs font-normal text-slate-500 px-2 py-1 bg-slate-700/50 rounded">{{ group.category }}</span>
|
|
1139
|
+
</h4>
|
|
1140
|
+
|
|
1141
|
+
<div class="overflow-x-auto">
|
|
1142
|
+
<table class="w-full">
|
|
1143
|
+
<thead>
|
|
1144
|
+
<tr class="border-b border-slate-700">
|
|
1145
|
+
<th class="text-left py-3 px-4 text-xs font-semibold text-slate-400 uppercase tracking-wider">{{ breakdown.dimensionLabel }}</th>
|
|
1146
|
+
<th class="text-right py-3 px-4 text-xs font-semibold text-slate-400 uppercase tracking-wider">Current</th>
|
|
1147
|
+
<th class="text-right py-3 px-4 text-xs font-semibold text-slate-400 uppercase tracking-wider">Trend</th>
|
|
1148
|
+
<th class="text-right py-3 px-4 text-xs font-semibold text-slate-400 uppercase tracking-wider">Status</th>
|
|
1149
|
+
</tr>
|
|
1150
|
+
</thead>
|
|
1151
|
+
<tbody>
|
|
1152
|
+
<ng-container *ngFor="let metric of group.values; let i = index">
|
|
1153
|
+
<tr class="hover:bg-slate-700/30 transition-colors">
|
|
1154
|
+
<td class="py-3 px-4 pb-1">
|
|
1155
|
+
<div>
|
|
1156
|
+
<div class="text-sm font-medium text-white">{{ formatDimensionValue(metric.dimensionValue) }}</div>
|
|
1157
|
+
<div class="text-xs text-slate-500 mt-1">Priority {{ metric.priority }}</div>
|
|
1158
|
+
</div>
|
|
1159
|
+
</td>
|
|
1160
|
+
<td class="text-right py-3 px-4 pb-1">
|
|
1161
|
+
<div class="text-sm font-semibold text-white">{{ formatValue(metric.currentValue || 0, metric.category, metric.metric) }}</div>
|
|
1162
|
+
</td>
|
|
1163
|
+
<td class="text-right py-3 px-4 pb-1">
|
|
1164
|
+
<div [class]="getTrendClass(metric)" class="text-sm font-semibold flex items-center justify-end gap-1">
|
|
1165
|
+
<svg *ngIf="metric.trendDirection === 'UP'" class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
|
1166
|
+
<path fill-rule="evenodd" d="M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
|
1167
|
+
</svg>
|
|
1168
|
+
<svg *ngIf="metric.trendDirection === 'DOWN'" class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
|
1169
|
+
<path fill-rule="evenodd" d="M14.707 10.293a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L9 12.586V5a1 1 0 012 0v7.586l2.293-2.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
|
1170
|
+
</svg>
|
|
1171
|
+
{{ Math.abs(metric.trendPercent || 0).toFixed(1) }}%
|
|
1172
|
+
</div>
|
|
1173
|
+
</td>
|
|
1174
|
+
<td class="text-right py-3 px-4 pb-1">
|
|
1175
|
+
<span [class]="getStatusBadgeClass(metric.status || 'ON_TRACK')" class="inline-block">
|
|
1176
|
+
{{ getStatusLabel(metric.status || 'ON_TRACK') }}
|
|
1177
|
+
</span>
|
|
1178
|
+
</td>
|
|
1179
|
+
</tr>
|
|
1180
|
+
<tr [class.border-b]="i < group.values.length - 1" class="border-slate-700">
|
|
1181
|
+
<td colspan="4" class="px-4 pt-0 pb-4">
|
|
1182
|
+
<div class="bg-slate-900/50 rounded-lg p-3 border border-slate-700/50">
|
|
1183
|
+
<p class="text-xs text-slate-400 leading-relaxed">{{ metric.description }}</p>
|
|
1184
|
+
</div>
|
|
1185
|
+
</td>
|
|
1186
|
+
</tr>
|
|
1187
|
+
</ng-container>
|
|
1188
|
+
</tbody>
|
|
1189
|
+
</table>
|
|
1190
|
+
</div>
|
|
1191
|
+
</div>
|
|
1192
|
+
</div>
|
|
1193
|
+
</div>
|
|
1194
|
+
`,
|
|
1195
|
+
}]
|
|
1196
|
+
}], null, { breakdown: [{
|
|
1197
|
+
type: Input
|
|
1198
|
+
}] }); })();
|
|
1199
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(BreakdownSectionComponent, { className: "BreakdownSectionComponent", filePath: "lib/components/breakdown-section.component.ts", lineNumber: 85 }); })();
|
|
1200
|
+
|
|
1201
|
+
function ModalComponent_div_0_div_14_li_21_Template(rf, ctx) { if (rf & 1) {
|
|
1202
|
+
i0.ɵɵelementStart(0, "li", 28);
|
|
1203
|
+
i0.ɵɵnamespaceSVG();
|
|
1204
|
+
i0.ɵɵelementStart(1, "svg", 29);
|
|
1205
|
+
i0.ɵɵelement(2, "path", 30);
|
|
1206
|
+
i0.ɵɵelementEnd();
|
|
1207
|
+
i0.ɵɵnamespaceHTML();
|
|
1208
|
+
i0.ɵɵelementStart(3, "span", 21);
|
|
1209
|
+
i0.ɵɵtext(4);
|
|
1210
|
+
i0.ɵɵelementEnd()();
|
|
1211
|
+
} if (rf & 2) {
|
|
1212
|
+
const rec_r3 = ctx.$implicit;
|
|
1213
|
+
i0.ɵɵadvance(4);
|
|
1214
|
+
i0.ɵɵtextInterpolate(rec_r3);
|
|
1215
|
+
} }
|
|
1216
|
+
function ModalComponent_div_0_div_14_span_26_Template(rf, ctx) { if (rf & 1) {
|
|
1217
|
+
i0.ɵɵelementStart(0, "span", 31);
|
|
1218
|
+
i0.ɵɵtext(1);
|
|
1219
|
+
i0.ɵɵelementEnd();
|
|
1220
|
+
} if (rf & 2) {
|
|
1221
|
+
const metric_r4 = ctx.$implicit;
|
|
1222
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
1223
|
+
i0.ɵɵadvance();
|
|
1224
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.formatMetricName(metric_r4), " ");
|
|
1225
|
+
} }
|
|
1226
|
+
function ModalComponent_div_0_div_14_Template(rf, ctx) { if (rf & 1) {
|
|
1227
|
+
i0.ɵɵelementStart(0, "div")(1, "div", 16)(2, "span", 17);
|
|
1228
|
+
i0.ɵɵtext(3);
|
|
1229
|
+
i0.ɵɵelementEnd();
|
|
1230
|
+
i0.ɵɵelementStart(4, "div", 18);
|
|
1231
|
+
i0.ɵɵtext(5);
|
|
1232
|
+
i0.ɵɵelementEnd()();
|
|
1233
|
+
i0.ɵɵelementStart(6, "div", 19)(7, "div")(8, "h4", 20);
|
|
1234
|
+
i0.ɵɵtext(9, "Description");
|
|
1235
|
+
i0.ɵɵelementEnd();
|
|
1236
|
+
i0.ɵɵelementStart(10, "p", 21);
|
|
1237
|
+
i0.ɵɵtext(11);
|
|
1238
|
+
i0.ɵɵelementEnd()();
|
|
1239
|
+
i0.ɵɵelementStart(12, "div")(13, "h4", 20);
|
|
1240
|
+
i0.ɵɵtext(14, "Business Context");
|
|
1241
|
+
i0.ɵɵelementEnd();
|
|
1242
|
+
i0.ɵɵelementStart(15, "p", 22);
|
|
1243
|
+
i0.ɵɵtext(16);
|
|
1244
|
+
i0.ɵɵelementEnd()();
|
|
1245
|
+
i0.ɵɵelementStart(17, "div")(18, "h4", 23);
|
|
1246
|
+
i0.ɵɵtext(19, "Recommendations");
|
|
1247
|
+
i0.ɵɵelementEnd();
|
|
1248
|
+
i0.ɵɵelementStart(20, "ul", 24);
|
|
1249
|
+
i0.ɵɵtemplate(21, ModalComponent_div_0_div_14_li_21_Template, 5, 1, "li", 25);
|
|
1250
|
+
i0.ɵɵelementEnd()();
|
|
1251
|
+
i0.ɵɵelementStart(22, "div")(23, "h4", 23);
|
|
1252
|
+
i0.ɵɵtext(24, "Related Metrics");
|
|
1253
|
+
i0.ɵɵelementEnd();
|
|
1254
|
+
i0.ɵɵelementStart(25, "div", 26);
|
|
1255
|
+
i0.ɵɵtemplate(26, ModalComponent_div_0_div_14_span_26_Template, 2, 1, "span", 27);
|
|
1256
|
+
i0.ɵɵelementEnd()()()();
|
|
1257
|
+
} if (rf & 2) {
|
|
1258
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
1259
|
+
i0.ɵɵadvance(3);
|
|
1260
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.insightData.priority, " ");
|
|
1261
|
+
i0.ɵɵadvance(2);
|
|
1262
|
+
i0.ɵɵtextInterpolate1("Priority ", ctx_r1.insightData.priority);
|
|
1263
|
+
i0.ɵɵadvance(6);
|
|
1264
|
+
i0.ɵɵtextInterpolate(ctx_r1.insightData.description);
|
|
1265
|
+
i0.ɵɵadvance(5);
|
|
1266
|
+
i0.ɵɵtextInterpolate(ctx_r1.insightData.businessContext);
|
|
1267
|
+
i0.ɵɵadvance(5);
|
|
1268
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.insightData.recommendations);
|
|
1269
|
+
i0.ɵɵadvance(5);
|
|
1270
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.insightData.relatedMetrics);
|
|
1271
|
+
} }
|
|
1272
|
+
function ModalComponent_div_0_div_15__svg_svg_8_Template(rf, ctx) { if (rf & 1) {
|
|
1273
|
+
i0.ɵɵnamespaceSVG();
|
|
1274
|
+
i0.ɵɵelementStart(0, "svg", 47);
|
|
1275
|
+
i0.ɵɵelement(1, "path", 48);
|
|
1276
|
+
i0.ɵɵelementEnd();
|
|
1277
|
+
} }
|
|
1278
|
+
function ModalComponent_div_0_div_15__svg_svg_9_Template(rf, ctx) { if (rf & 1) {
|
|
1279
|
+
i0.ɵɵnamespaceSVG();
|
|
1280
|
+
i0.ɵɵelementStart(0, "svg", 47);
|
|
1281
|
+
i0.ɵɵelement(1, "path", 49);
|
|
1282
|
+
i0.ɵɵelementEnd();
|
|
1283
|
+
} }
|
|
1284
|
+
function ModalComponent_div_0_div_15_Template(rf, ctx) { if (rf & 1) {
|
|
1285
|
+
i0.ɵɵelementStart(0, "div")(1, "div", 19)(2, "div", 7)(3, "div", 32)(4, "div", 33)(5, "span", 34);
|
|
1286
|
+
i0.ɵɵtext(6);
|
|
1287
|
+
i0.ɵɵelementEnd();
|
|
1288
|
+
i0.ɵɵelementStart(7, "span", 35);
|
|
1289
|
+
i0.ɵɵtemplate(8, ModalComponent_div_0_div_15__svg_svg_8_Template, 2, 0, "svg", 36)(9, ModalComponent_div_0_div_15__svg_svg_9_Template, 2, 0, "svg", 36);
|
|
1290
|
+
i0.ɵɵtext(10);
|
|
1291
|
+
i0.ɵɵelementEnd()();
|
|
1292
|
+
i0.ɵɵelementStart(11, "div", 37);
|
|
1293
|
+
i0.ɵɵtext(12);
|
|
1294
|
+
i0.ɵɵelementEnd()();
|
|
1295
|
+
i0.ɵɵelementStart(13, "div", 18)(14, "div");
|
|
1296
|
+
i0.ɵɵtext(15, "Priority: ");
|
|
1297
|
+
i0.ɵɵelementStart(16, "span", 38);
|
|
1298
|
+
i0.ɵɵtext(17);
|
|
1299
|
+
i0.ɵɵelementEnd()();
|
|
1300
|
+
i0.ɵɵelementStart(18, "div");
|
|
1301
|
+
i0.ɵɵtext(19, "Category: ");
|
|
1302
|
+
i0.ɵɵelementStart(20, "span", 38);
|
|
1303
|
+
i0.ɵɵtext(21);
|
|
1304
|
+
i0.ɵɵelementEnd()()()();
|
|
1305
|
+
i0.ɵɵelementStart(22, "div", 39)(23, "div", 40)(24, "div")(25, "div", 41);
|
|
1306
|
+
i0.ɵɵtext(26, "Target");
|
|
1307
|
+
i0.ɵɵelementEnd();
|
|
1308
|
+
i0.ɵɵelementStart(27, "div", 42);
|
|
1309
|
+
i0.ɵɵtext(28);
|
|
1310
|
+
i0.ɵɵelementEnd()();
|
|
1311
|
+
i0.ɵɵelementStart(29, "div")(30, "div", 41);
|
|
1312
|
+
i0.ɵɵtext(31, "Pacing");
|
|
1313
|
+
i0.ɵɵelementEnd();
|
|
1314
|
+
i0.ɵɵelementStart(32, "div", 43);
|
|
1315
|
+
i0.ɵɵtext(33);
|
|
1316
|
+
i0.ɵɵelementEnd()();
|
|
1317
|
+
i0.ɵɵelementStart(34, "div")(35, "div", 41);
|
|
1318
|
+
i0.ɵɵtext(36, "Prior YTD");
|
|
1319
|
+
i0.ɵɵelementEnd();
|
|
1320
|
+
i0.ɵɵelementStart(37, "div", 44);
|
|
1321
|
+
i0.ɵɵtext(38);
|
|
1322
|
+
i0.ɵɵelementEnd()();
|
|
1323
|
+
i0.ɵɵelementStart(39, "div")(40, "div", 41);
|
|
1324
|
+
i0.ɵɵtext(41, "Projected");
|
|
1325
|
+
i0.ɵɵelementEnd();
|
|
1326
|
+
i0.ɵɵelementStart(42, "div", 44);
|
|
1327
|
+
i0.ɵɵtext(43);
|
|
1328
|
+
i0.ɵɵelementEnd()()()();
|
|
1329
|
+
i0.ɵɵelementStart(44, "div", 45);
|
|
1330
|
+
i0.ɵɵelement(45, "div", 46);
|
|
1331
|
+
i0.ɵɵelementEnd();
|
|
1332
|
+
i0.ɵɵelementStart(46, "div")(47, "h4", 20);
|
|
1333
|
+
i0.ɵɵtext(48, "Analysis");
|
|
1334
|
+
i0.ɵɵelementEnd();
|
|
1335
|
+
i0.ɵɵelementStart(49, "p", 21);
|
|
1336
|
+
i0.ɵɵtext(50);
|
|
1337
|
+
i0.ɵɵelementEnd()()()();
|
|
1338
|
+
} if (rf & 2) {
|
|
1339
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
1340
|
+
i0.ɵɵadvance(6);
|
|
1341
|
+
i0.ɵɵtextInterpolate(ctx_r1.formatValue(ctx_r1.metricData.currentValue || 0));
|
|
1342
|
+
i0.ɵɵadvance();
|
|
1343
|
+
i0.ɵɵclassMap(ctx_r1.getTrendClass());
|
|
1344
|
+
i0.ɵɵadvance();
|
|
1345
|
+
i0.ɵɵproperty("ngIf", ctx_r1.metricData.trendDirection === "UP");
|
|
1346
|
+
i0.ɵɵadvance();
|
|
1347
|
+
i0.ɵɵproperty("ngIf", ctx_r1.metricData.trendDirection === "DOWN");
|
|
1348
|
+
i0.ɵɵadvance();
|
|
1349
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.Math.abs(ctx_r1.metricData.trendPercent || 0).toFixed(1), "% ");
|
|
1350
|
+
i0.ɵɵadvance();
|
|
1351
|
+
i0.ɵɵclassMap(ctx_r1.getStatusBadgeClass());
|
|
1352
|
+
i0.ɵɵadvance();
|
|
1353
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.getStatusLabel(), " ");
|
|
1354
|
+
i0.ɵɵadvance(5);
|
|
1355
|
+
i0.ɵɵtextInterpolate(ctx_r1.metricData.priority);
|
|
1356
|
+
i0.ɵɵadvance(4);
|
|
1357
|
+
i0.ɵɵtextInterpolate(ctx_r1.metricData.category);
|
|
1358
|
+
i0.ɵɵadvance(7);
|
|
1359
|
+
i0.ɵɵtextInterpolate(ctx_r1.formatValue(ctx_r1.metricData.targetValue || 0));
|
|
1360
|
+
i0.ɵɵadvance(4);
|
|
1361
|
+
i0.ɵɵclassProp("text-emerald-400", (ctx_r1.metricData.pacingPercentage || 0) > 0)("text-red-400", (ctx_r1.metricData.pacingPercentage || 0) < 0);
|
|
1362
|
+
i0.ɵɵadvance();
|
|
1363
|
+
i0.ɵɵtextInterpolate2(" ", (ctx_r1.metricData.pacingPercentage || 0) > 0 ? "+" : "", "", (ctx_r1.metricData.pacingPercentage || 0).toFixed(1), "% ");
|
|
1364
|
+
i0.ɵɵadvance(5);
|
|
1365
|
+
i0.ɵɵtextInterpolate(ctx_r1.formatValue(ctx_r1.metricData.priorYtdValue || 0));
|
|
1366
|
+
i0.ɵɵadvance(5);
|
|
1367
|
+
i0.ɵɵtextInterpolate(ctx_r1.formatValue(ctx_r1.metricData.projectedValue || 0));
|
|
1368
|
+
i0.ɵɵadvance(2);
|
|
1369
|
+
i0.ɵɵclassMap(ctx_r1.getPacingBarClass());
|
|
1370
|
+
i0.ɵɵstyleProp("width", ctx_r1.getPacingPercentage(), "%");
|
|
1371
|
+
i0.ɵɵadvance(5);
|
|
1372
|
+
i0.ɵɵtextInterpolate(ctx_r1.metricData.description);
|
|
1373
|
+
} }
|
|
1374
|
+
function ModalComponent_div_0_Template(rf, ctx) { if (rf & 1) {
|
|
1375
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
1376
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
1377
|
+
i0.ɵɵlistener("click", function ModalComponent_div_0_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.closeModal()); });
|
|
1378
|
+
i0.ɵɵelementStart(1, "div", 2);
|
|
1379
|
+
i0.ɵɵelement(2, "div", 3);
|
|
1380
|
+
i0.ɵɵelementStart(3, "span", 4);
|
|
1381
|
+
i0.ɵɵtext(4, "\u200B");
|
|
1382
|
+
i0.ɵɵelementEnd();
|
|
1383
|
+
i0.ɵɵelementStart(5, "div", 5);
|
|
1384
|
+
i0.ɵɵlistener("click", function ModalComponent_div_0_Template_div_click_5_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
1385
|
+
i0.ɵɵelementStart(6, "div", 6)(7, "div", 7)(8, "h3", 8);
|
|
1386
|
+
i0.ɵɵtext(9);
|
|
1387
|
+
i0.ɵɵelementEnd();
|
|
1388
|
+
i0.ɵɵelementStart(10, "button", 9);
|
|
1389
|
+
i0.ɵɵlistener("click", function ModalComponent_div_0_Template_button_click_10_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.closeModal()); });
|
|
1390
|
+
i0.ɵɵnamespaceSVG();
|
|
1391
|
+
i0.ɵɵelementStart(11, "svg", 10);
|
|
1392
|
+
i0.ɵɵelement(12, "path", 11);
|
|
1393
|
+
i0.ɵɵelementEnd()()()();
|
|
1394
|
+
i0.ɵɵnamespaceHTML();
|
|
1395
|
+
i0.ɵɵelementStart(13, "div", 12);
|
|
1396
|
+
i0.ɵɵtemplate(14, ModalComponent_div_0_div_14_Template, 27, 6, "div", 13)(15, ModalComponent_div_0_div_15_Template, 51, 25, "div", 13);
|
|
1397
|
+
i0.ɵɵelementEnd();
|
|
1398
|
+
i0.ɵɵelementStart(16, "div", 14)(17, "button", 15);
|
|
1399
|
+
i0.ɵɵlistener("click", function ModalComponent_div_0_Template_button_click_17_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.closeModal()); });
|
|
1400
|
+
i0.ɵɵtext(18, " Close ");
|
|
1401
|
+
i0.ɵɵelementEnd()()()()();
|
|
1402
|
+
} if (rf & 2) {
|
|
1403
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
1404
|
+
i0.ɵɵadvance(9);
|
|
1405
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.modalTitle, " ");
|
|
1406
|
+
i0.ɵɵadvance(5);
|
|
1407
|
+
i0.ɵɵproperty("ngIf", ctx_r1.modalType === "insight" && ctx_r1.insightData);
|
|
1408
|
+
i0.ɵɵadvance();
|
|
1409
|
+
i0.ɵɵproperty("ngIf", ctx_r1.modalType === "metric" && ctx_r1.metricData);
|
|
1410
|
+
} }
|
|
1411
|
+
class ModalComponent {
|
|
1412
|
+
constructor(modalService) {
|
|
1413
|
+
this.modalService = modalService;
|
|
1414
|
+
this.isOpen = false;
|
|
1415
|
+
this.modalType = null;
|
|
1416
|
+
this.insightData = null;
|
|
1417
|
+
this.metricData = null;
|
|
1418
|
+
this.Math = Math;
|
|
1419
|
+
}
|
|
1420
|
+
ngOnInit() {
|
|
1421
|
+
this.modalService.modalState$.subscribe(state => {
|
|
1422
|
+
this.isOpen = state.type !== null;
|
|
1423
|
+
this.modalType = state.type;
|
|
1424
|
+
if (state.type === 'insight') {
|
|
1425
|
+
this.insightData = state.data;
|
|
1426
|
+
this.metricData = null;
|
|
1427
|
+
}
|
|
1428
|
+
else if (state.type === 'metric') {
|
|
1429
|
+
this.metricData = state.data;
|
|
1430
|
+
this.insightData = null;
|
|
1431
|
+
}
|
|
1432
|
+
else {
|
|
1433
|
+
this.insightData = null;
|
|
1434
|
+
this.metricData = null;
|
|
1435
|
+
}
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
get modalTitle() {
|
|
1439
|
+
if (this.modalType === 'insight' && this.insightData) {
|
|
1440
|
+
return this.insightData.title || 'Insight';
|
|
1441
|
+
}
|
|
1442
|
+
if (this.modalType === 'metric' && this.metricData) {
|
|
1443
|
+
return this.formatMetricName(this.metricData.metric || 'UNKNOWN');
|
|
1444
|
+
}
|
|
1445
|
+
return '';
|
|
1446
|
+
}
|
|
1447
|
+
closeModal() {
|
|
1448
|
+
this.modalService.closeModal();
|
|
1449
|
+
}
|
|
1450
|
+
formatMetricName(name) {
|
|
1451
|
+
return name.replace(/_/g, ' ').toLowerCase().replace(/\b\w/g, l => l.toUpperCase());
|
|
1452
|
+
}
|
|
1453
|
+
formatValue(value) {
|
|
1454
|
+
if (!this.metricData)
|
|
1455
|
+
return value.toString();
|
|
1456
|
+
if ((this.metricData.category || '') === 'REVENUE') {
|
|
1457
|
+
if (value > 1000) {
|
|
1458
|
+
return '$' + value.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
|
1459
|
+
}
|
|
1460
|
+
return '$' + value.toFixed(2);
|
|
1461
|
+
}
|
|
1462
|
+
const metricName = this.metricData.metric || '';
|
|
1463
|
+
if (metricName.includes('RATE') || metricName.includes('CONVERSION')) {
|
|
1464
|
+
return value.toFixed(2) + '%';
|
|
1465
|
+
}
|
|
1466
|
+
if (value < 100 && value % 1 !== 0) {
|
|
1467
|
+
return value.toFixed(2);
|
|
1468
|
+
}
|
|
1469
|
+
return value.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
|
1470
|
+
}
|
|
1471
|
+
getTrendClass() {
|
|
1472
|
+
if (!this.metricData)
|
|
1473
|
+
return '';
|
|
1474
|
+
const isGoodTrend = (this.metricData.metric || '') === 'BOUNCE_RATE'
|
|
1475
|
+
? (this.metricData.trendDirection || '') === 'DOWN'
|
|
1476
|
+
: (this.metricData.trendDirection || '') === 'UP';
|
|
1477
|
+
return isGoodTrend ? 'text-emerald-400' : 'text-red-400';
|
|
1478
|
+
}
|
|
1479
|
+
getStatusBadgeClass() {
|
|
1480
|
+
if (!this.metricData)
|
|
1481
|
+
return '';
|
|
1482
|
+
const baseClass = 'px-3 py-1.5 rounded-full text-xs font-semibold';
|
|
1483
|
+
switch (this.metricData.status || 'ON_TRACK') {
|
|
1484
|
+
case 'OVERACHIEVING':
|
|
1485
|
+
return `${baseClass} bg-emerald-500/20 text-emerald-400 border border-emerald-500/30`;
|
|
1486
|
+
case 'ON_TRACK':
|
|
1487
|
+
return `${baseClass} bg-blue-500/20 text-blue-400 border border-blue-500/30`;
|
|
1488
|
+
case 'AT_RISK':
|
|
1489
|
+
return `${baseClass} bg-amber-500/20 text-amber-400 border border-amber-500/30`;
|
|
1490
|
+
default:
|
|
1491
|
+
return `${baseClass} bg-slate-500/20 text-slate-400 border border-slate-500/30`;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
getStatusLabel() {
|
|
1495
|
+
return (this.metricData?.status || '').replace(/_/g, ' ');
|
|
1496
|
+
}
|
|
1497
|
+
getPacingPercentage() {
|
|
1498
|
+
if (!this.metricData)
|
|
1499
|
+
return 0;
|
|
1500
|
+
const currentValue = this.metricData.currentValue || 0;
|
|
1501
|
+
const targetValue = this.metricData.targetValue || 1;
|
|
1502
|
+
const percentage = ((currentValue / targetValue) * 100);
|
|
1503
|
+
return Math.min(percentage, 100);
|
|
1504
|
+
}
|
|
1505
|
+
getPacingBarClass() {
|
|
1506
|
+
if (!this.metricData)
|
|
1507
|
+
return '';
|
|
1508
|
+
if (this.metricData.status === 'OVERACHIEVING') {
|
|
1509
|
+
return 'bg-gradient-to-r from-emerald-500 to-emerald-400';
|
|
1510
|
+
}
|
|
1511
|
+
if (this.metricData.status === 'AT_RISK') {
|
|
1512
|
+
return 'bg-gradient-to-r from-amber-500 to-amber-400';
|
|
1513
|
+
}
|
|
1514
|
+
return 'bg-gradient-to-r from-blue-500 to-blue-400';
|
|
1515
|
+
}
|
|
1516
|
+
static { this.ɵfac = function ModalComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ModalComponent)(i0.ɵɵdirectiveInject(ModalService)); }; }
|
|
1517
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ModalComponent, selectors: [["symphiq-funnel-analysis-modal"]], decls: 1, vars: 1, consts: [["class", "fixed inset-0 z-50 overflow-y-auto", 3, "click", 4, "ngIf"], [1, "fixed", "inset-0", "z-50", "overflow-y-auto", 3, "click"], [1, "flex", "items-center", "justify-center", "min-h-screen", "px-4", "pt-4", "pb-20", "text-center", "sm:block", "sm:p-0"], ["aria-hidden", "true", 1, "fixed", "inset-0", "transition-opacity", "bg-slate-950/80", "backdrop-blur-sm"], ["aria-hidden", "true", 1, "hidden", "sm:inline-block", "sm:align-middle", "sm:h-screen"], [1, "inline-block", "align-bottom", "bg-slate-800", "rounded-2xl", "text-left", "overflow-hidden", "shadow-xl", "transform", "transition-all", "sm:my-8", "sm:align-middle", "sm:max-w-3xl", "sm:w-full", "border", "border-slate-700", 3, "click"], [1, "bg-slate-800", "px-6", "py-5", "border-b", "border-slate-700"], [1, "flex", "items-start", "justify-between"], [1, "text-xl", "font-bold", "text-white"], [1, "text-slate-400", "hover:text-white", "transition-colors", "rounded-lg", "p-1", "hover:bg-slate-700", 3, "click"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-6", "h-6"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M6 18L18 6M6 6l12 12"], [1, "bg-slate-800", "px-6", "py-6", "max-h-[70vh]", "overflow-y-auto"], [4, "ngIf"], [1, "bg-slate-900/50", "px-6", "py-4", "border-t", "border-slate-700"], [1, "w-full", "px-4", "py-2", "bg-slate-700", "hover:bg-slate-600", "text-white", "rounded-lg", "transition-colors", "font-medium", 3, "click"], [1, "flex", "items-center", "gap-3", "mb-4"], [1, "inline-flex", "items-center", "justify-center", "w-8", "h-8", "rounded-full", "bg-blue-500/20", "text-blue-400", "text-sm", "font-bold"], [1, "text-sm", "text-slate-400"], [1, "space-y-6"], [1, "text-sm", "font-semibold", "text-slate-300", "uppercase", "tracking-wide", "mb-2"], [1, "text-slate-300", "leading-relaxed"], [1, "text-slate-400", "leading-relaxed"], [1, "text-sm", "font-semibold", "text-slate-300", "uppercase", "tracking-wide", "mb-3"], [1, "space-y-3"], ["class", "flex items-start gap-3 p-3 bg-slate-900/50 rounded-lg border border-slate-700/50", 4, "ngFor", "ngForOf"], [1, "flex", "flex-wrap", "gap-2"], ["class", "px-3 py-1.5 bg-slate-700/50 text-slate-300 rounded-lg text-sm font-medium border border-slate-600", 4, "ngFor", "ngForOf"], [1, "flex", "items-start", "gap-3", "p-3", "bg-slate-900/50", "rounded-lg", "border", "border-slate-700/50"], ["fill", "currentColor", "viewBox", "0 0 20 20", 1, "w-5", "h-5", "text-emerald-400", "mt-0.5", "flex-shrink-0"], ["fill-rule", "evenodd", "d", "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z", "clip-rule", "evenodd"], [1, "px-3", "py-1.5", "bg-slate-700/50", "text-slate-300", "rounded-lg", "text-sm", "font-medium", "border", "border-slate-600"], [1, "flex-1"], [1, "flex", "items-baseline", "gap-3", "mb-2"], [1, "text-4xl", "font-bold", "text-white"], [1, "text-lg", "font-semibold", "flex", "items-center", "gap-1"], ["class", "w-5 h-5", "fill", "currentColor", "viewBox", "0 0 20 20", 4, "ngIf"], [1, "inline-block"], [1, "text-white", "font-medium"], [1, "bg-slate-900/50", "rounded-lg", "p-4", "border", "border-slate-700/50"], [1, "grid", "grid-cols-2", "gap-4"], [1, "text-xs", "text-slate-500", "mb-1"], [1, "text-lg", "font-semibold", "text-white"], [1, "text-lg", "font-semibold"], [1, "text-lg", "font-semibold", "text-slate-300"], [1, "w-full", "bg-slate-700", "rounded-full", "h-3", "overflow-hidden"], [1, "h-3", "rounded-full", "transition-all", "duration-500"], ["fill", "currentColor", "viewBox", "0 0 20 20", 1, "w-5", "h-5"], ["fill-rule", "evenodd", "d", "M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z", "clip-rule", "evenodd"], ["fill-rule", "evenodd", "d", "M14.707 10.293a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L9 12.586V5a1 1 0 012 0v7.586l2.293-2.293a1 1 0 011.414 0z", "clip-rule", "evenodd"]], template: function ModalComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1518
|
+
i0.ɵɵtemplate(0, ModalComponent_div_0_Template, 19, 3, "div", 0);
|
|
1519
|
+
} if (rf & 2) {
|
|
1520
|
+
i0.ɵɵproperty("ngIf", ctx.isOpen);
|
|
1521
|
+
} }, dependencies: [CommonModule, i2.NgForOf, i2.NgIf], encapsulation: 2 }); }
|
|
1522
|
+
}
|
|
1523
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ModalComponent, [{
|
|
1524
|
+
type: Component,
|
|
1525
|
+
args: [{
|
|
1526
|
+
selector: 'symphiq-funnel-analysis-modal',
|
|
1527
|
+
standalone: true,
|
|
1528
|
+
imports: [CommonModule],
|
|
1529
|
+
template: `
|
|
1530
|
+
<div *ngIf="isOpen" class="fixed inset-0 z-50 overflow-y-auto" (click)="closeModal()">
|
|
1531
|
+
<div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
|
1532
|
+
<div class="fixed inset-0 transition-opacity bg-slate-950/80 backdrop-blur-sm" aria-hidden="true"></div>
|
|
1533
|
+
|
|
1534
|
+
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
|
1535
|
+
|
|
1536
|
+
<div
|
|
1537
|
+
(click)="$event.stopPropagation()"
|
|
1538
|
+
class="inline-block align-bottom bg-slate-800 rounded-2xl text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-3xl sm:w-full border border-slate-700">
|
|
1539
|
+
|
|
1540
|
+
<div class="bg-slate-800 px-6 py-5 border-b border-slate-700">
|
|
1541
|
+
<div class="flex items-start justify-between">
|
|
1542
|
+
<h3 class="text-xl font-bold text-white">
|
|
1543
|
+
{{ modalTitle }}
|
|
1544
|
+
</h3>
|
|
1545
|
+
<button
|
|
1546
|
+
(click)="closeModal()"
|
|
1547
|
+
class="text-slate-400 hover:text-white transition-colors rounded-lg p-1 hover:bg-slate-700">
|
|
1548
|
+
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
1549
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
1550
|
+
</svg>
|
|
1551
|
+
</button>
|
|
1552
|
+
</div>
|
|
1553
|
+
</div>
|
|
1554
|
+
|
|
1555
|
+
<div class="bg-slate-800 px-6 py-6 max-h-[70vh] overflow-y-auto">
|
|
1556
|
+
<div *ngIf="modalType === 'insight' && insightData">
|
|
1557
|
+
<div class="flex items-center gap-3 mb-4">
|
|
1558
|
+
<span class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-blue-500/20 text-blue-400 text-sm font-bold">
|
|
1559
|
+
{{ insightData.priority }}
|
|
1560
|
+
</span>
|
|
1561
|
+
<div class="text-sm text-slate-400">Priority {{ insightData.priority }}</div>
|
|
1562
|
+
</div>
|
|
1563
|
+
|
|
1564
|
+
<div class="space-y-6">
|
|
1565
|
+
<div>
|
|
1566
|
+
<h4 class="text-sm font-semibold text-slate-300 uppercase tracking-wide mb-2">Description</h4>
|
|
1567
|
+
<p class="text-slate-300 leading-relaxed">{{ insightData.description }}</p>
|
|
1568
|
+
</div>
|
|
1569
|
+
|
|
1570
|
+
<div>
|
|
1571
|
+
<h4 class="text-sm font-semibold text-slate-300 uppercase tracking-wide mb-2">Business Context</h4>
|
|
1572
|
+
<p class="text-slate-400 leading-relaxed">{{ insightData.businessContext }}</p>
|
|
1573
|
+
</div>
|
|
1574
|
+
|
|
1575
|
+
<div>
|
|
1576
|
+
<h4 class="text-sm font-semibold text-slate-300 uppercase tracking-wide mb-3">Recommendations</h4>
|
|
1577
|
+
<ul class="space-y-3">
|
|
1578
|
+
<li *ngFor="let rec of insightData.recommendations" class="flex items-start gap-3 p-3 bg-slate-900/50 rounded-lg border border-slate-700/50">
|
|
1579
|
+
<svg class="w-5 h-5 text-emerald-400 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
|
1580
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
|
1581
|
+
</svg>
|
|
1582
|
+
<span class="text-slate-300 leading-relaxed">{{ rec }}</span>
|
|
1583
|
+
</li>
|
|
1584
|
+
</ul>
|
|
1585
|
+
</div>
|
|
1586
|
+
|
|
1587
|
+
<div>
|
|
1588
|
+
<h4 class="text-sm font-semibold text-slate-300 uppercase tracking-wide mb-3">Related Metrics</h4>
|
|
1589
|
+
<div class="flex flex-wrap gap-2">
|
|
1590
|
+
<span *ngFor="let metric of insightData.relatedMetrics"
|
|
1591
|
+
class="px-3 py-1.5 bg-slate-700/50 text-slate-300 rounded-lg text-sm font-medium border border-slate-600">
|
|
1592
|
+
{{ formatMetricName(metric) }}
|
|
1593
|
+
</span>
|
|
1594
|
+
</div>
|
|
1595
|
+
</div>
|
|
1596
|
+
</div>
|
|
1597
|
+
</div>
|
|
1598
|
+
|
|
1599
|
+
<div *ngIf="modalType === 'metric' && metricData">
|
|
1600
|
+
<div class="space-y-6">
|
|
1601
|
+
<div class="flex items-start justify-between">
|
|
1602
|
+
<div class="flex-1">
|
|
1603
|
+
<div class="flex items-baseline gap-3 mb-2">
|
|
1604
|
+
<span class="text-4xl font-bold text-white">{{ formatValue(metricData.currentValue || 0) }}</span>
|
|
1605
|
+
<span [class]="getTrendClass()" class="text-lg font-semibold flex items-center gap-1">
|
|
1606
|
+
<svg *ngIf="metricData.trendDirection === 'UP'" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
|
1607
|
+
<path fill-rule="evenodd" d="M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
|
1608
|
+
</svg>
|
|
1609
|
+
<svg *ngIf="metricData.trendDirection === 'DOWN'" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
|
1610
|
+
<path fill-rule="evenodd" d="M14.707 10.293a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L9 12.586V5a1 1 0 012 0v7.586l2.293-2.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
|
1611
|
+
</svg>
|
|
1612
|
+
{{ Math.abs(metricData.trendPercent || 0).toFixed(1) }}%
|
|
1613
|
+
</span>
|
|
1614
|
+
</div>
|
|
1615
|
+
<div [class]="getStatusBadgeClass()" class="inline-block">
|
|
1616
|
+
{{ getStatusLabel() }}
|
|
1617
|
+
</div>
|
|
1618
|
+
</div>
|
|
1619
|
+
<div class="text-sm text-slate-400">
|
|
1620
|
+
<div>Priority: <span class="text-white font-medium">{{ metricData.priority }}</span></div>
|
|
1621
|
+
<div>Category: <span class="text-white font-medium">{{ metricData.category }}</span></div>
|
|
1622
|
+
</div>
|
|
1623
|
+
</div>
|
|
1624
|
+
|
|
1625
|
+
<div class="bg-slate-900/50 rounded-lg p-4 border border-slate-700/50">
|
|
1626
|
+
<div class="grid grid-cols-2 gap-4">
|
|
1627
|
+
<div>
|
|
1628
|
+
<div class="text-xs text-slate-500 mb-1">Target</div>
|
|
1629
|
+
<div class="text-lg font-semibold text-white">{{ formatValue(metricData.targetValue || 0) }}</div>
|
|
1630
|
+
</div>
|
|
1631
|
+
<div>
|
|
1632
|
+
<div class="text-xs text-slate-500 mb-1">Pacing</div>
|
|
1633
|
+
<div class="text-lg font-semibold" [class.text-emerald-400]="(metricData.pacingPercentage || 0) > 0" [class.text-red-400]="(metricData.pacingPercentage || 0) < 0">
|
|
1634
|
+
{{ (metricData.pacingPercentage || 0) > 0 ? '+' : '' }}{{ (metricData.pacingPercentage || 0).toFixed(1) }}%
|
|
1635
|
+
</div>
|
|
1636
|
+
</div>
|
|
1637
|
+
<div>
|
|
1638
|
+
<div class="text-xs text-slate-500 mb-1">Prior YTD</div>
|
|
1639
|
+
<div class="text-lg font-semibold text-slate-300">{{ formatValue(metricData.priorYtdValue || 0) }}</div>
|
|
1640
|
+
</div>
|
|
1641
|
+
<div>
|
|
1642
|
+
<div class="text-xs text-slate-500 mb-1">Projected</div>
|
|
1643
|
+
<div class="text-lg font-semibold text-slate-300">{{ formatValue(metricData.projectedValue || 0) }}</div>
|
|
1644
|
+
</div>
|
|
1645
|
+
</div>
|
|
1646
|
+
</div>
|
|
1647
|
+
|
|
1648
|
+
<div class="w-full bg-slate-700 rounded-full h-3 overflow-hidden">
|
|
1649
|
+
<div
|
|
1650
|
+
[style.width.%]="getPacingPercentage()"
|
|
1651
|
+
[class]="getPacingBarClass()"
|
|
1652
|
+
class="h-3 rounded-full transition-all duration-500">
|
|
1653
|
+
</div>
|
|
1654
|
+
</div>
|
|
1655
|
+
|
|
1656
|
+
<div>
|
|
1657
|
+
<h4 class="text-sm font-semibold text-slate-300 uppercase tracking-wide mb-2">Analysis</h4>
|
|
1658
|
+
<p class="text-slate-300 leading-relaxed">{{ metricData.description }}</p>
|
|
1659
|
+
</div>
|
|
1660
|
+
</div>
|
|
1661
|
+
</div>
|
|
1662
|
+
</div>
|
|
1663
|
+
|
|
1664
|
+
<div class="bg-slate-900/50 px-6 py-4 border-t border-slate-700">
|
|
1665
|
+
<button
|
|
1666
|
+
(click)="closeModal()"
|
|
1667
|
+
class="w-full px-4 py-2 bg-slate-700 hover:bg-slate-600 text-white rounded-lg transition-colors font-medium">
|
|
1668
|
+
Close
|
|
1669
|
+
</button>
|
|
1670
|
+
</div>
|
|
1671
|
+
</div>
|
|
1672
|
+
</div>
|
|
1673
|
+
</div>
|
|
1674
|
+
`,
|
|
1675
|
+
}]
|
|
1676
|
+
}], () => [{ type: ModalService }], null); })();
|
|
1677
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ModalComponent, { className: "ModalComponent", filePath: "lib/components/modal.component.ts", lineNumber: 157 }); })();
|
|
1678
|
+
|
|
1679
|
+
const PERFORMANCE_DATA = ({
|
|
1680
|
+
schemaVersion: 3,
|
|
1681
|
+
"overallAssessment": {
|
|
1682
|
+
"priorYearTrend": "Strong growth compared to prior year, with revenue up 33% and traffic nearly doubling, but conversion rates and engagement efficiency have declined.",
|
|
1683
|
+
"keyStrengths": "Exceptional top-of-funnel growth, high average order value, and strong desktop and organic/direct channel performance.",
|
|
1684
|
+
"areasForImprovement": "Conversion rates, add to cart efficiency, and mobile/tablet engagement are underperforming; bounce rate is elevated, especially on non-desktop devices and paid/email channels.",
|
|
1685
|
+
"grade": "B",
|
|
1686
|
+
"narrative": "The business is experiencing robust growth in traffic and revenue, driven by strong brand awareness and high-value transactions, particularly among trade and luxury buyers. However, conversion rates and engagement efficiency have declined, with mobile and paid channels underperforming and bounce rates rising. To achieve 2025 targets, the shop must address conversion friction, optimize mobile/tablet experiences, and refine channel strategies to better capture high-intent buyers.",
|
|
1687
|
+
"recommendedActions": "Prioritize conversion optimization (especially on mobile/tablet), streamline checkout, refine paid and email targeting, and leverage segmentation to tailor engagement for trade and homeowner segments.",
|
|
1688
|
+
"overallStatus": "ON_TRACK",
|
|
1689
|
+
"targetPacingStatus": "Falling slightly behind target pace; revenue and purchases are close to target but conversion rates and engagement metrics need improvement."
|
|
1690
|
+
},
|
|
1691
|
+
"insights": [
|
|
1692
|
+
{
|
|
1693
|
+
"relatedMetrics": [
|
|
1694
|
+
"ECOMMERCE_CONVERSION_RATE",
|
|
1695
|
+
"ADD_TO_CART_RATE",
|
|
1696
|
+
"ACTIVE_USER_ADD_TO_CART_RATE",
|
|
1697
|
+
"BOUNCE_RATE",
|
|
1698
|
+
"PURCHASE_REVENUE"
|
|
1699
|
+
],
|
|
1700
|
+
"businessContext": "Artisan decorative lighting with high AOV, project-based sales cycles, and a mix of trade and homeowner customers. Desktop dominates revenue, but mobile/tablet are growing in volume.",
|
|
1701
|
+
"description": "Conversion rates have declined significantly YoY and are pacing well below target, despite strong top-of-funnel growth. The largest leakage points are from product views to add to cart and from add to cart to checkout, especially on mobile and paid channels. High bounce rates further indicate friction in the user journey.",
|
|
1702
|
+
"title": "Conversion Friction Is Limiting Revenue Growth Despite Strong Traffic",
|
|
1703
|
+
"priority": 1,
|
|
1704
|
+
"recommendations": [
|
|
1705
|
+
"Conduct a UX audit focused on mobile/tablet to identify and remove friction in add to cart and checkout flows.",
|
|
1706
|
+
"Streamline checkout steps and highlight trust signals (testimonials, artisan stories) to reduce abandonment.",
|
|
1707
|
+
"Implement persistent cart and save-for-later features to capture more mid-funnel intent."
|
|
1708
|
+
]
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
"relatedMetrics": [
|
|
1712
|
+
"SESSIONS",
|
|
1713
|
+
"SCREEN_PAGE_VIEWS",
|
|
1714
|
+
"NEW_USERS",
|
|
1715
|
+
"ACTIVE_USERS"
|
|
1716
|
+
],
|
|
1717
|
+
"businessContext": "Brand awareness and organic reach are strong, with significant YoY growth in sessions and new users. Trade and design professionals are key segments.",
|
|
1718
|
+
"description": "Top-of-funnel metrics have nearly doubled YoY, driven by organic and direct channels. However, this growth is not translating into proportional increases in purchases or revenue, indicating a need to better qualify and engage high-intent visitors.",
|
|
1719
|
+
"title": "Top-of-Funnel Growth Outpaces Lower-Funnel Performance",
|
|
1720
|
+
"priority": 2,
|
|
1721
|
+
"recommendations": [
|
|
1722
|
+
"Refine paid and organic campaigns to focus on high-intent, project-based keywords and audiences.",
|
|
1723
|
+
"Personalize landing pages and product recommendations based on traffic source and customer segment.",
|
|
1724
|
+
"Expand organic content around industry trends, customization, and project case studies."
|
|
1725
|
+
]
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
"relatedMetrics": [
|
|
1729
|
+
"BOUNCE_RATE",
|
|
1730
|
+
"PRODUCT_VIEW_RATE",
|
|
1731
|
+
"PRODUCT_VIEW_CONVERSION_RATE"
|
|
1732
|
+
],
|
|
1733
|
+
"businessContext": "Mobile and tablet segments are growing in volume but have high bounce rates and low conversion efficiency, especially among homeowners and specifiers.",
|
|
1734
|
+
"description": "Bounce rate has increased 7 points YoY and is 18% above target, with mobile/tablet and paid/email channels most affected. Product view rates are also below target, indicating missed engagement opportunities.",
|
|
1735
|
+
"title": "Elevated Bounce Rate and Low Engagement on Mobile/Tablet",
|
|
1736
|
+
"priority": 2,
|
|
1737
|
+
"recommendations": [
|
|
1738
|
+
"Simplify mobile navigation and prioritize key content for mobile/tablet users.",
|
|
1739
|
+
"Implement engagement tactics such as exit-intent popups and live chat.",
|
|
1740
|
+
"Test mobile-specific landing pages and offers for quick-ship and ready-to-ship products."
|
|
1741
|
+
]
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
"relatedMetrics": [
|
|
1745
|
+
"REVENUE_PER_ADD_TO_CART",
|
|
1746
|
+
"REVENUE_PER_CHECKOUT",
|
|
1747
|
+
"AVERAGE_ORDER_VALUE"
|
|
1748
|
+
],
|
|
1749
|
+
"businessContext": "High-value transactions are driven by trade and luxury buyers, with desktop and organic/direct channels leading in revenue efficiency.",
|
|
1750
|
+
"description": "Revenue per add to cart, per checkout, and average order value are all above target, reflecting strong appeal to high-value segments. However, low conversion rates limit the impact of these strengths.",
|
|
1751
|
+
"title": "High-Value Transactions Highlight Brand Strength, But Volume Is Lacking",
|
|
1752
|
+
"priority": 3,
|
|
1753
|
+
"recommendations": [
|
|
1754
|
+
"Deepen partnerships with design professionals and hospitality clients through referral and organic channels.",
|
|
1755
|
+
"Leverage storytelling and behind-the-scenes content to reinforce artisan craftsmanship.",
|
|
1756
|
+
"Offer incentives or exclusive previews to trade and repeat buyers to increase purchase frequency."
|
|
1757
|
+
]
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
"relatedMetrics": [
|
|
1761
|
+
"ECOMMERCE_CONVERSION_RATE",
|
|
1762
|
+
"ADD_TO_CART_RATE",
|
|
1763
|
+
"CHECKOUT_CONVERSION_RATE"
|
|
1764
|
+
],
|
|
1765
|
+
"businessContext": "Data quality is strong, with no missing or zero values detected. Seasonal and project-based cycles drive expected volatility.",
|
|
1766
|
+
"description": "Data integrity is high, supporting reliable analysis and targeted optimization. Volatility in conversion rates aligns with project-based sales cycles.",
|
|
1767
|
+
"title": "Data Quality Supports Reliable Optimization",
|
|
1768
|
+
"priority": 4,
|
|
1769
|
+
"recommendations": [
|
|
1770
|
+
"Continue regular data audits, especially when launching new campaigns or site features.",
|
|
1771
|
+
"Validate tracking for new product launches and custom order flows.",
|
|
1772
|
+
"Monitor segment-specific metrics to measure the impact of targeted initiatives."
|
|
1773
|
+
]
|
|
1774
|
+
}
|
|
1775
|
+
],
|
|
1776
|
+
"breakdowns": [
|
|
1777
|
+
{
|
|
1778
|
+
"dimensionType": "device",
|
|
1779
|
+
"dimensionLabel": "Device",
|
|
1780
|
+
"summaryAnalysis": "Desktop dominates revenue (87%) and conversions, with the highest engagement and lowest bounce rate. Mobile and tablet segments are growing in traffic but underperform in conversion and revenue efficiency, with high bounce rates and low add to cart rates. Tablet, while small in volume, shows occasional high-value orders. Optimizing mobile/tablet experiences is critical to capturing more value from these segments.",
|
|
1781
|
+
"subMetrics": [
|
|
1782
|
+
{
|
|
1783
|
+
"priorYtdValue": 270776,
|
|
1784
|
+
"pacingPercentage": 55.6,
|
|
1785
|
+
"trendPercent": 35.6,
|
|
1786
|
+
"description": "Drives the most sessions and engagement, supporting strong revenue growth.",
|
|
1787
|
+
"ytdVariancePercent": 35.6,
|
|
1788
|
+
"priority": 1,
|
|
1789
|
+
"projectedValue": 563000,
|
|
1790
|
+
"dimensionValue": "DESKTOP",
|
|
1791
|
+
"metric": "SESSIONS",
|
|
1792
|
+
"variance": 0,
|
|
1793
|
+
"trendDirection": "UP",
|
|
1794
|
+
"targetValue": 0,
|
|
1795
|
+
"category": "ENGAGEMENT",
|
|
1796
|
+
"currentValue": 367051,
|
|
1797
|
+
"status": "ON_TRACK"
|
|
1798
|
+
},
|
|
1799
|
+
{
|
|
1800
|
+
"priorYtdValue": 440110,
|
|
1801
|
+
"pacingPercentage": 5,
|
|
1802
|
+
"trendPercent": 30.3,
|
|
1803
|
+
"description": "Dominates revenue and conversions, reflecting strong appeal to trade and luxury buyers.",
|
|
1804
|
+
"ytdVariancePercent": 30.3,
|
|
1805
|
+
"priority": 1,
|
|
1806
|
+
"projectedValue": 573262,
|
|
1807
|
+
"dimensionValue": "DESKTOP",
|
|
1808
|
+
"metric": "PURCHASE_REVENUE",
|
|
1809
|
+
"variance": 273262,
|
|
1810
|
+
"trendDirection": "UP",
|
|
1811
|
+
"targetValue": 0,
|
|
1812
|
+
"category": "REVENUE",
|
|
1813
|
+
"currentValue": 573262,
|
|
1814
|
+
"status": "OVERACHIEVING"
|
|
1815
|
+
},
|
|
1816
|
+
{
|
|
1817
|
+
"priorYtdValue": 0.03545,
|
|
1818
|
+
"pacingPercentage": -28.6,
|
|
1819
|
+
"trendPercent": -30,
|
|
1820
|
+
"description": "Strongest conversion rate among devices, but down YoY and below target.",
|
|
1821
|
+
"ytdVariancePercent": -30,
|
|
1822
|
+
"priority": 2,
|
|
1823
|
+
"projectedValue": 0.02479,
|
|
1824
|
+
"dimensionValue": "DESKTOP",
|
|
1825
|
+
"metric": "ECOMMERCE_CONVERSION_RATE",
|
|
1826
|
+
"variance": -0.01066,
|
|
1827
|
+
"trendDirection": "DOWN",
|
|
1828
|
+
"targetValue": 0.03545,
|
|
1829
|
+
"category": "CONVERSION",
|
|
1830
|
+
"currentValue": 0.02479,
|
|
1831
|
+
"status": "ON_TRACK"
|
|
1832
|
+
},
|
|
1833
|
+
{
|
|
1834
|
+
"priorYtdValue": 48.5,
|
|
1835
|
+
"pacingPercentage": -1,
|
|
1836
|
+
"trendPercent": 14.5,
|
|
1837
|
+
"description": "Lowest bounce rate, supporting high engagement and conversion.",
|
|
1838
|
+
"ytdVariancePercent": 14.5,
|
|
1839
|
+
"priority": 2,
|
|
1840
|
+
"projectedValue": 55.5,
|
|
1841
|
+
"dimensionValue": "DESKTOP",
|
|
1842
|
+
"metric": "BOUNCE_RATE",
|
|
1843
|
+
"variance": 7,
|
|
1844
|
+
"trendDirection": "UP",
|
|
1845
|
+
"targetValue": 48.5,
|
|
1846
|
+
"category": "ENGAGEMENT",
|
|
1847
|
+
"currentValue": 55.5,
|
|
1848
|
+
"status": "ON_TRACK"
|
|
1849
|
+
},
|
|
1850
|
+
{
|
|
1851
|
+
"priorYtdValue": 3504.3,
|
|
1852
|
+
"pacingPercentage": 19,
|
|
1853
|
+
"trendPercent": 80,
|
|
1854
|
+
"description": "Highest average order value, reflecting high-value transactions.",
|
|
1855
|
+
"ytdVariancePercent": 80,
|
|
1856
|
+
"priority": 1,
|
|
1857
|
+
"projectedValue": 6299.6,
|
|
1858
|
+
"dimensionValue": "DESKTOP",
|
|
1859
|
+
"metric": "AVERAGE_ORDER_VALUE",
|
|
1860
|
+
"variance": 2795.3,
|
|
1861
|
+
"trendDirection": "UP",
|
|
1862
|
+
"targetValue": 3504.3,
|
|
1863
|
+
"category": "REVENUE",
|
|
1864
|
+
"currentValue": 6299.6,
|
|
1865
|
+
"status": "OVERACHIEVING"
|
|
1866
|
+
},
|
|
1867
|
+
{
|
|
1868
|
+
"priorYtdValue": 247011,
|
|
1869
|
+
"pacingPercentage": 103,
|
|
1870
|
+
"trendPercent": 103,
|
|
1871
|
+
"description": "Mobile traffic is up significantly, but conversion and revenue efficiency lag.",
|
|
1872
|
+
"ytdVariancePercent": 103,
|
|
1873
|
+
"priority": 2,
|
|
1874
|
+
"projectedValue": 501561,
|
|
1875
|
+
"dimensionValue": "MOBILE",
|
|
1876
|
+
"metric": "SESSIONS",
|
|
1877
|
+
"variance": 254550,
|
|
1878
|
+
"trendDirection": "UP",
|
|
1879
|
+
"targetValue": 247011,
|
|
1880
|
+
"category": "ENGAGEMENT",
|
|
1881
|
+
"currentValue": 501561,
|
|
1882
|
+
"status": "ON_TRACK"
|
|
1883
|
+
},
|
|
1884
|
+
{
|
|
1885
|
+
"priorYtdValue": 56069,
|
|
1886
|
+
"pacingPercentage": 16,
|
|
1887
|
+
"trendPercent": 16,
|
|
1888
|
+
"description": "Revenue is up, but mobile remains a small share of total revenue.",
|
|
1889
|
+
"ytdVariancePercent": 16,
|
|
1890
|
+
"priority": 2,
|
|
1891
|
+
"projectedValue": 65009,
|
|
1892
|
+
"dimensionValue": "MOBILE",
|
|
1893
|
+
"metric": "PURCHASE_REVENUE",
|
|
1894
|
+
"variance": 8940,
|
|
1895
|
+
"trendDirection": "UP",
|
|
1896
|
+
"targetValue": 56069,
|
|
1897
|
+
"category": "REVENUE",
|
|
1898
|
+
"currentValue": 65009,
|
|
1899
|
+
"status": "ON_TRACK"
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
"priorYtdValue": 0.00648,
|
|
1903
|
+
"pacingPercentage": -35,
|
|
1904
|
+
"trendPercent": -35,
|
|
1905
|
+
"description": "Conversion rate is well below desktop and target, indicating friction.",
|
|
1906
|
+
"ytdVariancePercent": -35,
|
|
1907
|
+
"priority": 3,
|
|
1908
|
+
"projectedValue": 0.00419,
|
|
1909
|
+
"dimensionValue": "MOBILE",
|
|
1910
|
+
"metric": "ECOMMERCE_CONVERSION_RATE",
|
|
1911
|
+
"variance": -0.00229,
|
|
1912
|
+
"trendDirection": "DOWN",
|
|
1913
|
+
"targetValue": 0.00648,
|
|
1914
|
+
"category": "CONVERSION",
|
|
1915
|
+
"currentValue": 0.00419,
|
|
1916
|
+
"status": "AT_RISK"
|
|
1917
|
+
},
|
|
1918
|
+
{
|
|
1919
|
+
"priorYtdValue": 66.3,
|
|
1920
|
+
"pacingPercentage": 6.2,
|
|
1921
|
+
"trendPercent": 6.2,
|
|
1922
|
+
"description": "Bounce rate is high, limiting engagement and conversion.",
|
|
1923
|
+
"ytdVariancePercent": 6.2,
|
|
1924
|
+
"priority": 3,
|
|
1925
|
+
"projectedValue": 70.4,
|
|
1926
|
+
"dimensionValue": "MOBILE",
|
|
1927
|
+
"metric": "BOUNCE_RATE",
|
|
1928
|
+
"variance": 4.1,
|
|
1929
|
+
"trendDirection": "UP",
|
|
1930
|
+
"targetValue": 66.3,
|
|
1931
|
+
"category": "ENGAGEMENT",
|
|
1932
|
+
"currentValue": 70.4,
|
|
1933
|
+
"status": "AT_RISK"
|
|
1934
|
+
},
|
|
1935
|
+
{
|
|
1936
|
+
"priorYtdValue": 3504.3,
|
|
1937
|
+
"pacingPercentage": -11.7,
|
|
1938
|
+
"trendPercent": -11.7,
|
|
1939
|
+
"description": "Average order value is down YoY, reflecting lower-value mobile transactions.",
|
|
1940
|
+
"ytdVariancePercent": -11.7,
|
|
1941
|
+
"priority": 3,
|
|
1942
|
+
"projectedValue": 3095.7,
|
|
1943
|
+
"dimensionValue": "MOBILE",
|
|
1944
|
+
"metric": "AVERAGE_ORDER_VALUE",
|
|
1945
|
+
"variance": -408.6,
|
|
1946
|
+
"trendDirection": "DOWN",
|
|
1947
|
+
"targetValue": 3504.3,
|
|
1948
|
+
"category": "REVENUE",
|
|
1949
|
+
"currentValue": 3095.7,
|
|
1950
|
+
"status": "AT_RISK"
|
|
1951
|
+
},
|
|
1952
|
+
{
|
|
1953
|
+
"priorYtdValue": 66,
|
|
1954
|
+
"pacingPercentage": 76,
|
|
1955
|
+
"trendPercent": 76,
|
|
1956
|
+
"description": "Tablet traffic is up, but conversion and revenue are volatile due to low volume.",
|
|
1957
|
+
"ytdVariancePercent": 76,
|
|
1958
|
+
"priority": 4,
|
|
1959
|
+
"projectedValue": 116,
|
|
1960
|
+
"dimensionValue": "TABLET",
|
|
1961
|
+
"metric": "SESSIONS",
|
|
1962
|
+
"variance": 50,
|
|
1963
|
+
"trendDirection": "UP",
|
|
1964
|
+
"targetValue": 66,
|
|
1965
|
+
"category": "ENGAGEMENT",
|
|
1966
|
+
"currentValue": 57033,
|
|
1967
|
+
"status": "ON_TRACK"
|
|
1968
|
+
},
|
|
1969
|
+
{
|
|
1970
|
+
"priorYtdValue": 0,
|
|
1971
|
+
"pacingPercentage": 0,
|
|
1972
|
+
"trendPercent": 0,
|
|
1973
|
+
"description": "Tablet revenue is up, with occasional high-value orders.",
|
|
1974
|
+
"ytdVariancePercent": 0,
|
|
1975
|
+
"priority": 4,
|
|
1976
|
+
"projectedValue": 21467,
|
|
1977
|
+
"dimensionValue": "TABLET",
|
|
1978
|
+
"metric": "PURCHASE_REVENUE",
|
|
1979
|
+
"variance": 21467,
|
|
1980
|
+
"trendDirection": "UP",
|
|
1981
|
+
"targetValue": 0,
|
|
1982
|
+
"category": "REVENUE",
|
|
1983
|
+
"currentValue": 21467,
|
|
1984
|
+
"status": "ON_TRACK"
|
|
1985
|
+
}
|
|
1986
|
+
]
|
|
1987
|
+
},
|
|
1988
|
+
{
|
|
1989
|
+
"dimensionType": "medium",
|
|
1990
|
+
"dimensionLabel": "Channel/Medium",
|
|
1991
|
+
"summaryAnalysis": "Organic and direct channels deliver the highest engagement and revenue efficiency, with strong product view rates and revenue per product view. Paid (CPC) drives the most sessions but underperforms in conversion and revenue per visit. Referral and email channels show high value per transaction but low volume and high bounce rates. Optimizing paid and email for high-intent, trade-focused campaigns is critical.",
|
|
1992
|
+
"subMetrics": [
|
|
1993
|
+
{
|
|
1994
|
+
"priorYtdValue": 79525,
|
|
1995
|
+
"pacingPercentage": 0,
|
|
1996
|
+
"trendPercent": 0,
|
|
1997
|
+
"description": "Organic sessions are stable YoY, supporting strong engagement and revenue.",
|
|
1998
|
+
"ytdVariancePercent": 0,
|
|
1999
|
+
"priority": 1,
|
|
2000
|
+
"projectedValue": 79232,
|
|
2001
|
+
"dimensionValue": "organic",
|
|
2002
|
+
"metric": "SESSIONS",
|
|
2003
|
+
"variance": 0,
|
|
2004
|
+
"trendDirection": "STABLE",
|
|
2005
|
+
"targetValue": 0,
|
|
2006
|
+
"category": "ENGAGEMENT",
|
|
2007
|
+
"currentValue": 79232,
|
|
2008
|
+
"status": "ON_TRACK"
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
"priorYtdValue": 218557,
|
|
2012
|
+
"pacingPercentage": 10.2,
|
|
2013
|
+
"trendPercent": 10.2,
|
|
2014
|
+
"description": "Paid (CPC) sessions are up YoY, but conversion and revenue efficiency lag.",
|
|
2015
|
+
"ytdVariancePercent": 10.2,
|
|
2016
|
+
"priority": 2,
|
|
2017
|
+
"projectedValue": 310395,
|
|
2018
|
+
"dimensionValue": "cpc",
|
|
2019
|
+
"metric": "SESSIONS",
|
|
2020
|
+
"variance": 91838,
|
|
2021
|
+
"trendDirection": "UP",
|
|
2022
|
+
"targetValue": 218557,
|
|
2023
|
+
"category": "ENGAGEMENT",
|
|
2024
|
+
"currentValue": 310395,
|
|
2025
|
+
"status": "ON_TRACK"
|
|
2026
|
+
},
|
|
2027
|
+
{
|
|
2028
|
+
"priorYtdValue": 23335,
|
|
2029
|
+
"pacingPercentage": 4,
|
|
2030
|
+
"trendPercent": 4,
|
|
2031
|
+
"description": "Referral sessions are up slightly, with high value per transaction but low volume.",
|
|
2032
|
+
"ytdVariancePercent": 4,
|
|
2033
|
+
"priority": 3,
|
|
2034
|
+
"projectedValue": 243207,
|
|
2035
|
+
"dimensionValue": "referral",
|
|
2036
|
+
"metric": "SESSIONS",
|
|
2037
|
+
"variance": 9872,
|
|
2038
|
+
"trendDirection": "UP",
|
|
2039
|
+
"targetValue": 23335,
|
|
2040
|
+
"category": "ENGAGEMENT",
|
|
2041
|
+
"currentValue": 243207,
|
|
2042
|
+
"status": "ON_TRACK"
|
|
2043
|
+
},
|
|
2044
|
+
{
|
|
2045
|
+
"priorYtdValue": 25160,
|
|
2046
|
+
"pacingPercentage": 228.9,
|
|
2047
|
+
"trendPercent": 228.9,
|
|
2048
|
+
"description": "Email sessions have increased dramatically, but conversion rates are low.",
|
|
2049
|
+
"ytdVariancePercent": 228.9,
|
|
2050
|
+
"priority": 3,
|
|
2051
|
+
"projectedValue": 82801,
|
|
2052
|
+
"dimensionValue": "email",
|
|
2053
|
+
"metric": "SESSIONS",
|
|
2054
|
+
"variance": 57641,
|
|
2055
|
+
"trendDirection": "UP",
|
|
2056
|
+
"targetValue": 25160,
|
|
2057
|
+
"category": "ENGAGEMENT",
|
|
2058
|
+
"currentValue": 82801,
|
|
2059
|
+
"status": "ON_TRACK"
|
|
2060
|
+
},
|
|
2061
|
+
{
|
|
2062
|
+
"priorYtdValue": 98217,
|
|
2063
|
+
"pacingPercentage": 9.7,
|
|
2064
|
+
"trendPercent": 9.7,
|
|
2065
|
+
"description": "Direct sessions are up, supporting strong engagement and revenue efficiency.",
|
|
2066
|
+
"ytdVariancePercent": 9.7,
|
|
2067
|
+
"priority": 2,
|
|
2068
|
+
"projectedValue": 197563,
|
|
2069
|
+
"dimensionValue": "(none)",
|
|
2070
|
+
"metric": "SESSIONS",
|
|
2071
|
+
"variance": 99346,
|
|
2072
|
+
"trendDirection": "UP",
|
|
2073
|
+
"targetValue": 98217,
|
|
2074
|
+
"category": "ENGAGEMENT",
|
|
2075
|
+
"currentValue": 197563,
|
|
2076
|
+
"status": "ON_TRACK"
|
|
2077
|
+
},
|
|
2078
|
+
{
|
|
2079
|
+
"priorYtdValue": 0.705,
|
|
2080
|
+
"pacingPercentage": 14.5,
|
|
2081
|
+
"trendPercent": 14.5,
|
|
2082
|
+
"description": "Organic revenue per product view is highest among channels, supporting efficient revenue growth.",
|
|
2083
|
+
"ytdVariancePercent": 14.5,
|
|
2084
|
+
"priority": 1,
|
|
2085
|
+
"projectedValue": 0.807,
|
|
2086
|
+
"dimensionValue": "organic",
|
|
2087
|
+
"metric": "REVENUE_PER_PRODUCT_VIEW",
|
|
2088
|
+
"variance": 0.102,
|
|
2089
|
+
"trendDirection": "UP",
|
|
2090
|
+
"targetValue": 0.705,
|
|
2091
|
+
"category": "REVENUE",
|
|
2092
|
+
"currentValue": 0.807,
|
|
2093
|
+
"status": "OVERACHIEVING"
|
|
2094
|
+
},
|
|
2095
|
+
{
|
|
2096
|
+
"priorYtdValue": 0.277,
|
|
2097
|
+
"pacingPercentage": 6.3,
|
|
2098
|
+
"trendPercent": 6.3,
|
|
2099
|
+
"description": "Paid (CPC) revenue per product view is up slightly, but remains below organic and referral.",
|
|
2100
|
+
"ytdVariancePercent": 6.3,
|
|
2101
|
+
"priority": 2,
|
|
2102
|
+
"projectedValue": 0.295,
|
|
2103
|
+
"dimensionValue": "cpc",
|
|
2104
|
+
"metric": "REVENUE_PER_PRODUCT_VIEW",
|
|
2105
|
+
"variance": 0.018,
|
|
2106
|
+
"trendDirection": "UP",
|
|
2107
|
+
"targetValue": 0.277,
|
|
2108
|
+
"category": "REVENUE",
|
|
2109
|
+
"currentValue": 0.295,
|
|
2110
|
+
"status": "ON_TRACK"
|
|
2111
|
+
},
|
|
2112
|
+
{
|
|
2113
|
+
"priorYtdValue": 0.828,
|
|
2114
|
+
"pacingPercentage": 139.2,
|
|
2115
|
+
"trendPercent": 139.2,
|
|
2116
|
+
"description": "Referral revenue per product view is up sharply, reflecting high-value transactions.",
|
|
2117
|
+
"ytdVariancePercent": 139.2,
|
|
2118
|
+
"priority": 2,
|
|
2119
|
+
"projectedValue": 1.982,
|
|
2120
|
+
"dimensionValue": "referral",
|
|
2121
|
+
"metric": "REVENUE_PER_PRODUCT_VIEW",
|
|
2122
|
+
"variance": 1.154,
|
|
2123
|
+
"trendDirection": "UP",
|
|
2124
|
+
"targetValue": 0.828,
|
|
2125
|
+
"category": "REVENUE",
|
|
2126
|
+
"currentValue": 1.982,
|
|
2127
|
+
"status": "OVERACHIEVING"
|
|
2128
|
+
}
|
|
2129
|
+
]
|
|
2130
|
+
},
|
|
2131
|
+
{
|
|
2132
|
+
"dimensionType": "source",
|
|
2133
|
+
"dimensionLabel": "Traffic Source",
|
|
2134
|
+
"summaryAnalysis": "Google is the largest source of traffic and revenue, but conversion rates are below target and declining YoY. Direct and organic sources deliver the highest engagement and revenue efficiency. Bing and Yahoo, while small in volume, show very high revenue per add to cart and per product view. Facebook and email underperform in both engagement and revenue, with high bounce rates and low conversion rates. Strategic focus should be on expanding high-value sources and optimizing underperforming channels.",
|
|
2135
|
+
"subMetrics": [
|
|
2136
|
+
{
|
|
2137
|
+
"priorYtdValue": 231224,
|
|
2138
|
+
"pacingPercentage": 24.7,
|
|
2139
|
+
"trendPercent": 24.7,
|
|
2140
|
+
"description": "Google drives the most sessions and revenue, but conversion rates are below target.",
|
|
2141
|
+
"ytdVariancePercent": 24.7,
|
|
2142
|
+
"priority": 1,
|
|
2143
|
+
"projectedValue": 288335,
|
|
2144
|
+
"dimensionValue": "google",
|
|
2145
|
+
"metric": "SESSIONS",
|
|
2146
|
+
"variance": 57111,
|
|
2147
|
+
"trendDirection": "UP",
|
|
2148
|
+
"targetValue": 231224,
|
|
2149
|
+
"category": "ENGAGEMENT",
|
|
2150
|
+
"currentValue": 288335,
|
|
2151
|
+
"status": "ON_TRACK"
|
|
2152
|
+
},
|
|
2153
|
+
{
|
|
2154
|
+
"priorYtdValue": 98217,
|
|
2155
|
+
"pacingPercentage": 101.5,
|
|
2156
|
+
"trendPercent": 101.5,
|
|
2157
|
+
"description": "Direct sessions are up, supporting strong engagement and revenue efficiency.",
|
|
2158
|
+
"ytdVariancePercent": 101.5,
|
|
2159
|
+
"priority": 2,
|
|
2160
|
+
"projectedValue": 197563,
|
|
2161
|
+
"dimensionValue": "(direct)",
|
|
2162
|
+
"metric": "SESSIONS",
|
|
2163
|
+
"variance": 99346,
|
|
2164
|
+
"trendDirection": "UP",
|
|
2165
|
+
"targetValue": 98217,
|
|
2166
|
+
"category": "ENGAGEMENT",
|
|
2167
|
+
"currentValue": 197563,
|
|
2168
|
+
"status": "ON_TRACK"
|
|
2169
|
+
},
|
|
2170
|
+
{
|
|
2171
|
+
"priorYtdValue": 34396,
|
|
2172
|
+
"pacingPercentage": 80.9,
|
|
2173
|
+
"trendPercent": 80.9,
|
|
2174
|
+
"description": "Bing revenue is up sharply, with high revenue per add to cart and per product view.",
|
|
2175
|
+
"ytdVariancePercent": 80.9,
|
|
2176
|
+
"priority": 2,
|
|
2177
|
+
"projectedValue": 62258,
|
|
2178
|
+
"dimensionValue": "bing",
|
|
2179
|
+
"metric": "PURCHASE_REVENUE",
|
|
2180
|
+
"variance": 27862,
|
|
2181
|
+
"trendDirection": "UP",
|
|
2182
|
+
"targetValue": 34396,
|
|
2183
|
+
"category": "REVENUE",
|
|
2184
|
+
"currentValue": 62258,
|
|
2185
|
+
"status": "OVERACHIEVING"
|
|
2186
|
+
},
|
|
2187
|
+
{
|
|
2188
|
+
"priorYtdValue": 267408,
|
|
2189
|
+
"pacingPercentage": 17.1,
|
|
2190
|
+
"trendPercent": 17.1,
|
|
2191
|
+
"description": "Google revenue is up, but conversion rates are below target.",
|
|
2192
|
+
"ytdVariancePercent": 17.1,
|
|
2193
|
+
"priority": 1,
|
|
2194
|
+
"projectedValue": 313196,
|
|
2195
|
+
"dimensionValue": "google",
|
|
2196
|
+
"metric": "PURCHASE_REVENUE",
|
|
2197
|
+
"variance": 45788,
|
|
2198
|
+
"trendDirection": "UP",
|
|
2199
|
+
"targetValue": 267408,
|
|
2200
|
+
"category": "REVENUE",
|
|
2201
|
+
"currentValue": 313196,
|
|
2202
|
+
"status": "ON_TRACK"
|
|
2203
|
+
},
|
|
2204
|
+
{
|
|
2205
|
+
"priorYtdValue": 57628,
|
|
2206
|
+
"pacingPercentage": -52,
|
|
2207
|
+
"trendPercent": -52,
|
|
2208
|
+
"description": "Klaviyo (email) revenue is down, with low volume and conversion rates.",
|
|
2209
|
+
"ytdVariancePercent": -52,
|
|
2210
|
+
"priority": 3,
|
|
2211
|
+
"projectedValue": 27659,
|
|
2212
|
+
"dimensionValue": "Klaviyo",
|
|
2213
|
+
"metric": "PURCHASE_REVENUE",
|
|
2214
|
+
"variance": -29969,
|
|
2215
|
+
"trendDirection": "DOWN",
|
|
2216
|
+
"targetValue": 57628,
|
|
2217
|
+
"category": "REVENUE",
|
|
2218
|
+
"currentValue": 27659,
|
|
2219
|
+
"status": "AT_RISK"
|
|
2220
|
+
},
|
|
2221
|
+
{
|
|
2222
|
+
"priorYtdValue": 0.4618,
|
|
2223
|
+
"pacingPercentage": -8.2,
|
|
2224
|
+
"trendPercent": -8.2,
|
|
2225
|
+
"description": "Google revenue per product view is slightly down YoY, reflecting lower efficiency.",
|
|
2226
|
+
"ytdVariancePercent": -8.2,
|
|
2227
|
+
"priority": 2,
|
|
2228
|
+
"projectedValue": 0.424,
|
|
2229
|
+
"dimensionValue": "google",
|
|
2230
|
+
"metric": "REVENUE_PER_PRODUCT_VIEW",
|
|
2231
|
+
"variance": -0.0378,
|
|
2232
|
+
"trendDirection": "DOWN",
|
|
2233
|
+
"targetValue": 0.4618,
|
|
2234
|
+
"category": "REVENUE",
|
|
2235
|
+
"currentValue": 0.424,
|
|
2236
|
+
"status": "AT_RISK"
|
|
2237
|
+
},
|
|
2238
|
+
{
|
|
2239
|
+
"priorYtdValue": 0.4551,
|
|
2240
|
+
"pacingPercentage": -10.1,
|
|
2241
|
+
"trendPercent": -10.1,
|
|
2242
|
+
"description": "Direct revenue per product view is slightly down YoY, but remains above paid and social.",
|
|
2243
|
+
"ytdVariancePercent": -10.1,
|
|
2244
|
+
"priority": 2,
|
|
2245
|
+
"projectedValue": 0.409,
|
|
2246
|
+
"dimensionValue": "(direct)",
|
|
2247
|
+
"metric": "REVENUE_PER_PRODUCT_VIEW",
|
|
2248
|
+
"variance": -0.0461,
|
|
2249
|
+
"trendDirection": "DOWN",
|
|
2250
|
+
"targetValue": 0.4551,
|
|
2251
|
+
"category": "REVENUE",
|
|
2252
|
+
"currentValue": 0.409,
|
|
2253
|
+
"status": "ON_TRACK"
|
|
2254
|
+
}
|
|
2255
|
+
]
|
|
2256
|
+
}
|
|
2257
|
+
],
|
|
2258
|
+
"generatedAt": "2025-11-30T23:59:59Z",
|
|
2259
|
+
"metrics": [
|
|
2260
|
+
{
|
|
2261
|
+
"priorYtdValue": 549415,
|
|
2262
|
+
"pacingPercentage": 60.3,
|
|
2263
|
+
"trendPercent": 68.4,
|
|
2264
|
+
"description": "Sessions are up significantly YoY, reflecting strong brand awareness and top-of-funnel growth.",
|
|
2265
|
+
"ytdVariancePercent": 68.4,
|
|
2266
|
+
"priority": 1,
|
|
2267
|
+
"projectedValue": 1056358,
|
|
2268
|
+
"dimensionValue": "",
|
|
2269
|
+
"metric": "SESSIONS",
|
|
2270
|
+
"variance": 265796,
|
|
2271
|
+
"trendDirection": "UP",
|
|
2272
|
+
"targetValue": 659278.2,
|
|
2273
|
+
"category": "ENGAGEMENT",
|
|
2274
|
+
"currentValue": 925074,
|
|
2275
|
+
"status": "OVERACHIEVING"
|
|
2276
|
+
},
|
|
2277
|
+
{
|
|
2278
|
+
"priorYtdValue": 458580,
|
|
2279
|
+
"pacingPercentage": 69.9,
|
|
2280
|
+
"trendPercent": 78.6,
|
|
2281
|
+
"description": "Active users have nearly doubled YoY, supporting strong engagement and reach.",
|
|
2282
|
+
"ytdVariancePercent": 78.6,
|
|
2283
|
+
"priority": 1,
|
|
2284
|
+
"projectedValue": 936295,
|
|
2285
|
+
"dimensionValue": "",
|
|
2286
|
+
"metric": "ACTIVE_USERS",
|
|
2287
|
+
"variance": 367900,
|
|
2288
|
+
"trendDirection": "UP",
|
|
2289
|
+
"targetValue": 551075.7,
|
|
2290
|
+
"category": "ENGAGEMENT",
|
|
2291
|
+
"currentValue": 818976,
|
|
2292
|
+
"status": "OVERACHIEVING"
|
|
2293
|
+
},
|
|
2294
|
+
{
|
|
2295
|
+
"priorYtdValue": 496179,
|
|
2296
|
+
"pacingPercentage": -0.2,
|
|
2297
|
+
"trendPercent": 33,
|
|
2298
|
+
"description": "Revenue is up 33% YoY and pacing just below the 2025 target.",
|
|
2299
|
+
"ytdVariancePercent": 33,
|
|
2300
|
+
"priority": 1,
|
|
2301
|
+
"projectedValue": 708898,
|
|
2302
|
+
"dimensionValue": "",
|
|
2303
|
+
"metric": "PURCHASE_REVENUE",
|
|
2304
|
+
"variance": -17740,
|
|
2305
|
+
"trendDirection": "UP",
|
|
2306
|
+
"targetValue": 710637.85,
|
|
2307
|
+
"category": "REVENUE",
|
|
2308
|
+
"currentValue": 659742,
|
|
2309
|
+
"status": "ON_TRACK"
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
"priorYtdValue": 389639,
|
|
2313
|
+
"pacingPercentage": 80.3,
|
|
2314
|
+
"trendPercent": 89.7,
|
|
2315
|
+
"description": "New user acquisition is up sharply YoY, reflecting strong brand reach.",
|
|
2316
|
+
"ytdVariancePercent": 89.7,
|
|
2317
|
+
"priority": 2,
|
|
2318
|
+
"projectedValue": 848043,
|
|
2319
|
+
"dimensionValue": "",
|
|
2320
|
+
"metric": "NEW_USERS",
|
|
2321
|
+
"variance": 268332,
|
|
2322
|
+
"trendDirection": "UP",
|
|
2323
|
+
"targetValue": 470660.4,
|
|
2324
|
+
"category": "ENGAGEMENT",
|
|
2325
|
+
"currentValue": 738992,
|
|
2326
|
+
"status": "OVERACHIEVING"
|
|
2327
|
+
},
|
|
2328
|
+
{
|
|
2329
|
+
"priorYtdValue": 1343076,
|
|
2330
|
+
"pacingPercentage": 21.8,
|
|
2331
|
+
"trendPercent": 29.6,
|
|
2332
|
+
"description": "Page views are up nearly 30% YoY, supporting strong engagement.",
|
|
2333
|
+
"ytdVariancePercent": 29.6,
|
|
2334
|
+
"priority": 2,
|
|
2335
|
+
"projectedValue": 1957736,
|
|
2336
|
+
"dimensionValue": "",
|
|
2337
|
+
"metric": "SCREEN_PAGE_VIEWS",
|
|
2338
|
+
"variance": 133736,
|
|
2339
|
+
"trendDirection": "UP",
|
|
2340
|
+
"targetValue": 1606999.87,
|
|
2341
|
+
"category": "ENGAGEMENT",
|
|
2342
|
+
"currentValue": 1740174,
|
|
2343
|
+
"status": "OVERACHIEVING"
|
|
2344
|
+
},
|
|
2345
|
+
{
|
|
2346
|
+
"priorYtdValue": 57.55,
|
|
2347
|
+
"pacingPercentage": 18.2,
|
|
2348
|
+
"trendPercent": 12.4,
|
|
2349
|
+
"description": "Bounce rate has increased YoY and is pacing 18% above target, especially on mobile/tablet and paid/email channels.",
|
|
2350
|
+
"ytdVariancePercent": 12.4,
|
|
2351
|
+
"priority": 1,
|
|
2352
|
+
"projectedValue": 65.09,
|
|
2353
|
+
"dimensionValue": "",
|
|
2354
|
+
"metric": "BOUNCE_RATE",
|
|
2355
|
+
"variance": 9.67,
|
|
2356
|
+
"trendDirection": "UP",
|
|
2357
|
+
"targetValue": 55.02,
|
|
2358
|
+
"category": "ENGAGEMENT",
|
|
2359
|
+
"currentValue": 64.69,
|
|
2360
|
+
"status": "AT_RISK"
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
"priorYtdValue": 7389,
|
|
2364
|
+
"pacingPercentage": -0.2,
|
|
2365
|
+
"trendPercent": 20.8,
|
|
2366
|
+
"description": "Add to carts are up 21% YoY and pacing at target, reflecting strong product interest but not translating into increased purchases.",
|
|
2367
|
+
"ytdVariancePercent": 20.8,
|
|
2368
|
+
"priority": 2,
|
|
2369
|
+
"projectedValue": 9912,
|
|
2370
|
+
"dimensionValue": "",
|
|
2371
|
+
"metric": "ADD_TO_CARTS",
|
|
2372
|
+
"variance": -21,
|
|
2373
|
+
"trendDirection": "UP",
|
|
2374
|
+
"targetValue": 9932.71,
|
|
2375
|
+
"category": "CONVERSION",
|
|
2376
|
+
"currentValue": 8926,
|
|
2377
|
+
"status": "ON_TRACK"
|
|
2378
|
+
},
|
|
2379
|
+
{
|
|
2380
|
+
"priorYtdValue": 795,
|
|
2381
|
+
"pacingPercentage": -6.6,
|
|
2382
|
+
"trendPercent": 16.1,
|
|
2383
|
+
"description": "Checkouts are up 16% YoY but pacing 7% below target.",
|
|
2384
|
+
"ytdVariancePercent": 16.1,
|
|
2385
|
+
"priority": 2,
|
|
2386
|
+
"projectedValue": 1023,
|
|
2387
|
+
"dimensionValue": "",
|
|
2388
|
+
"metric": "CHECKOUTS",
|
|
2389
|
+
"variance": -72,
|
|
2390
|
+
"trendDirection": "UP",
|
|
2391
|
+
"targetValue": 1094.72,
|
|
2392
|
+
"category": "CONVERSION",
|
|
2393
|
+
"currentValue": 923,
|
|
2394
|
+
"status": "ON_TRACK"
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
"priorYtdValue": 112,
|
|
2398
|
+
"pacingPercentage": -21.2,
|
|
2399
|
+
"trendPercent": 2.7,
|
|
2400
|
+
"description": "Purchases are flat YoY and projected to end the year 21% below target.",
|
|
2401
|
+
"ytdVariancePercent": 2.7,
|
|
2402
|
+
"priority": 1,
|
|
2403
|
+
"projectedValue": 126,
|
|
2404
|
+
"dimensionValue": "",
|
|
2405
|
+
"metric": "ECOMMERCE_PURCHASES",
|
|
2406
|
+
"variance": -34,
|
|
2407
|
+
"trendDirection": "STABLE",
|
|
2408
|
+
"targetValue": 160.41,
|
|
2409
|
+
"category": "CONVERSION",
|
|
2410
|
+
"currentValue": 115,
|
|
2411
|
+
"status": "AT_RISK"
|
|
2412
|
+
},
|
|
2413
|
+
{
|
|
2414
|
+
"priorYtdValue": 0.0204,
|
|
2415
|
+
"pacingPercentage": -40.2,
|
|
2416
|
+
"trendPercent": -39,
|
|
2417
|
+
"description": "Ecommerce conversion rate is down 39% YoY and pacing 40% below target.",
|
|
2418
|
+
"ytdVariancePercent": -39,
|
|
2419
|
+
"priority": 1,
|
|
2420
|
+
"projectedValue": 0.012,
|
|
2421
|
+
"dimensionValue": "",
|
|
2422
|
+
"metric": "ECOMMERCE_CONVERSION_RATE",
|
|
2423
|
+
"variance": -0.008,
|
|
2424
|
+
"trendDirection": "DOWN",
|
|
2425
|
+
"targetValue": 0.02,
|
|
2426
|
+
"category": "CONVERSION",
|
|
2427
|
+
"currentValue": 0.0124,
|
|
2428
|
+
"status": "AT_RISK"
|
|
2429
|
+
},
|
|
2430
|
+
{
|
|
2431
|
+
"priorYtdValue": 4430.17,
|
|
2432
|
+
"pacingPercentage": 25.2,
|
|
2433
|
+
"trendPercent": 29.5,
|
|
2434
|
+
"description": "Average order value is up 29.5% YoY and pacing 25% above target, reflecting high-value transactions.",
|
|
2435
|
+
"ytdVariancePercent": 29.5,
|
|
2436
|
+
"priority": 1,
|
|
2437
|
+
"projectedValue": 5608.85,
|
|
2438
|
+
"dimensionValue": "",
|
|
2439
|
+
"metric": "AVERAGE_ORDER_VALUE",
|
|
2440
|
+
"variance": 1256.55,
|
|
2441
|
+
"trendDirection": "UP",
|
|
2442
|
+
"targetValue": 4480.33,
|
|
2443
|
+
"category": "REVENUE",
|
|
2444
|
+
"currentValue": 5736.89,
|
|
2445
|
+
"status": "OVERACHIEVING"
|
|
2446
|
+
},
|
|
2447
|
+
{
|
|
2448
|
+
"priorYtdValue": 985900,
|
|
2449
|
+
"pacingPercentage": 12.5,
|
|
2450
|
+
"trendPercent": 26,
|
|
2451
|
+
"description": "Product views are up 26% YoY, supporting strong engagement.",
|
|
2452
|
+
"ytdVariancePercent": 26,
|
|
2453
|
+
"priority": 2,
|
|
2454
|
+
"projectedValue": 1383667,
|
|
2455
|
+
"dimensionValue": "",
|
|
2456
|
+
"metric": "ITEM_VIEW_EVENTS",
|
|
2457
|
+
"variance": 152941,
|
|
2458
|
+
"trendDirection": "UP",
|
|
2459
|
+
"targetValue": 1229726.49,
|
|
2460
|
+
"category": "ENGAGEMENT",
|
|
2461
|
+
"currentValue": 1242036,
|
|
2462
|
+
"status": "OVERACHIEVING"
|
|
2463
|
+
},
|
|
2464
|
+
{
|
|
2465
|
+
"priorYtdValue": 0.01136,
|
|
2466
|
+
"pacingPercentage": -8.7,
|
|
2467
|
+
"trendPercent": -18.5,
|
|
2468
|
+
"description": "Product view conversion rate is down YoY and pacing below target.",
|
|
2469
|
+
"ytdVariancePercent": -18.5,
|
|
2470
|
+
"priority": 2,
|
|
2471
|
+
"projectedValue": 0.00913,
|
|
2472
|
+
"dimensionValue": "",
|
|
2473
|
+
"metric": "PRODUCT_VIEW_CONVERSION_RATE",
|
|
2474
|
+
"variance": -0.0011,
|
|
2475
|
+
"trendDirection": "DOWN",
|
|
2476
|
+
"targetValue": 0.01,
|
|
2477
|
+
"category": "CONVERSION",
|
|
2478
|
+
"currentValue": 0.00926,
|
|
2479
|
+
"status": "AT_RISK"
|
|
2480
|
+
},
|
|
2481
|
+
{
|
|
2482
|
+
"priorYtdValue": 1.3449,
|
|
2483
|
+
"pacingPercentage": -34.4,
|
|
2484
|
+
"trendPercent": -28.2,
|
|
2485
|
+
"description": "Add to cart rate is down 28% YoY and pacing 34% below target.",
|
|
2486
|
+
"ytdVariancePercent": -28.2,
|
|
2487
|
+
"priority": 2,
|
|
2488
|
+
"projectedValue": 0.938,
|
|
2489
|
+
"dimensionValue": "",
|
|
2490
|
+
"metric": "ADD_TO_CART_RATE",
|
|
2491
|
+
"variance": -0.491,
|
|
2492
|
+
"trendDirection": "DOWN",
|
|
2493
|
+
"targetValue": 1.43,
|
|
2494
|
+
"category": "CONVERSION",
|
|
2495
|
+
"currentValue": 0.9649,
|
|
2496
|
+
"status": "AT_RISK"
|
|
2497
|
+
},
|
|
2498
|
+
{
|
|
2499
|
+
"priorYtdValue": 1.6113,
|
|
2500
|
+
"pacingPercentage": -38.1,
|
|
2501
|
+
"trendPercent": -32.4,
|
|
2502
|
+
"description": "Active user add to cart rate is down 32% YoY and pacing 38% below target.",
|
|
2503
|
+
"ytdVariancePercent": -32.4,
|
|
2504
|
+
"priority": 2,
|
|
2505
|
+
"projectedValue": 1.059,
|
|
2506
|
+
"dimensionValue": "",
|
|
2507
|
+
"metric": "ACTIVE_USER_ADD_TO_CART_RATE",
|
|
2508
|
+
"variance": -0.651,
|
|
2509
|
+
"trendDirection": "DOWN",
|
|
2510
|
+
"targetValue": 1.71,
|
|
2511
|
+
"category": "CONVERSION",
|
|
2512
|
+
"currentValue": 1.0899,
|
|
2513
|
+
"status": "AT_RISK"
|
|
2514
|
+
},
|
|
2515
|
+
{
|
|
2516
|
+
"priorYtdValue": 0.1734,
|
|
2517
|
+
"pacingPercentage": -39.3,
|
|
2518
|
+
"trendPercent": -35,
|
|
2519
|
+
"description": "Active user checkout rate is down 35% YoY and pacing 39% below target.",
|
|
2520
|
+
"ytdVariancePercent": -35,
|
|
2521
|
+
"priority": 2,
|
|
2522
|
+
"projectedValue": 0.1092,
|
|
2523
|
+
"dimensionValue": "",
|
|
2524
|
+
"metric": "ACTIVE_USER_CHECKOUT_RATE",
|
|
2525
|
+
"variance": -0.0708,
|
|
2526
|
+
"trendDirection": "DOWN",
|
|
2527
|
+
"targetValue": 0.18,
|
|
2528
|
+
"category": "CONVERSION",
|
|
2529
|
+
"currentValue": 0.1127,
|
|
2530
|
+
"status": "AT_RISK"
|
|
2531
|
+
},
|
|
2532
|
+
{
|
|
2533
|
+
"priorYtdValue": 0.5033,
|
|
2534
|
+
"pacingPercentage": 1.2,
|
|
2535
|
+
"trendPercent": 5.5,
|
|
2536
|
+
"description": "Revenue per product view is up 5.5% YoY and pacing just below target.",
|
|
2537
|
+
"ytdVariancePercent": 5.5,
|
|
2538
|
+
"priority": 1,
|
|
2539
|
+
"projectedValue": 0.5123,
|
|
2540
|
+
"dimensionValue": "",
|
|
2541
|
+
"metric": "REVENUE_PER_PRODUCT_VIEW",
|
|
2542
|
+
"variance": -0.0077,
|
|
2543
|
+
"trendDirection": "UP",
|
|
2544
|
+
"targetValue": 0.52,
|
|
2545
|
+
"category": "REVENUE",
|
|
2546
|
+
"currentValue": 0.5312,
|
|
2547
|
+
"status": "ON_TRACK"
|
|
2548
|
+
},
|
|
2549
|
+
{
|
|
2550
|
+
"priorYtdValue": 67.15,
|
|
2551
|
+
"pacingPercentage": 5.7,
|
|
2552
|
+
"trendPercent": 10.1,
|
|
2553
|
+
"description": "Revenue per add to cart is up 10% YoY and pacing 5.7% above target.",
|
|
2554
|
+
"ytdVariancePercent": 10.1,
|
|
2555
|
+
"priority": 2,
|
|
2556
|
+
"projectedValue": 71.52,
|
|
2557
|
+
"dimensionValue": "",
|
|
2558
|
+
"metric": "REVENUE_PER_ADD_TO_CART",
|
|
2559
|
+
"variance": 3.83,
|
|
2560
|
+
"trendDirection": "UP",
|
|
2561
|
+
"targetValue": 67.69,
|
|
2562
|
+
"category": "REVENUE",
|
|
2563
|
+
"currentValue": 73.91,
|
|
2564
|
+
"status": "OVERACHIEVING"
|
|
2565
|
+
},
|
|
2566
|
+
{
|
|
2567
|
+
"priorYtdValue": 624.12,
|
|
2568
|
+
"pacingPercentage": 7.8,
|
|
2569
|
+
"trendPercent": 14.5,
|
|
2570
|
+
"description": "Revenue per checkout is up 14.5% YoY and pacing 7.8% above target.",
|
|
2571
|
+
"ytdVariancePercent": 14.5,
|
|
2572
|
+
"priority": 2,
|
|
2573
|
+
"projectedValue": 693.14,
|
|
2574
|
+
"dimensionValue": "",
|
|
2575
|
+
"metric": "REVENUE_PER_CHECKOUT",
|
|
2576
|
+
"variance": 50.24,
|
|
2577
|
+
"trendDirection": "UP",
|
|
2578
|
+
"targetValue": 642.9,
|
|
2579
|
+
"category": "REVENUE",
|
|
2580
|
+
"currentValue": 714.78,
|
|
2581
|
+
"status": "OVERACHIEVING"
|
|
2582
|
+
},
|
|
2583
|
+
{
|
|
2584
|
+
"priorYtdValue": 73.41,
|
|
2585
|
+
"pacingPercentage": -7.9,
|
|
2586
|
+
"trendPercent": -3.6,
|
|
2587
|
+
"description": "View to product view conversion rate is down YoY and pacing below target.",
|
|
2588
|
+
"ytdVariancePercent": -3.6,
|
|
2589
|
+
"priority": 3,
|
|
2590
|
+
"projectedValue": 70.68,
|
|
2591
|
+
"dimensionValue": "",
|
|
2592
|
+
"metric": "VIEW_TO_PRODUCT_VIEW_CONVERSION_RATE",
|
|
2593
|
+
"variance": -6.07,
|
|
2594
|
+
"trendDirection": "DOWN",
|
|
2595
|
+
"targetValue": 76.75,
|
|
2596
|
+
"category": "ENGAGEMENT",
|
|
2597
|
+
"currentValue": 71.37,
|
|
2598
|
+
"status": "AT_RISK"
|
|
2599
|
+
},
|
|
2600
|
+
{
|
|
2601
|
+
"priorYtdValue": 0.7495,
|
|
2602
|
+
"pacingPercentage": -11.6,
|
|
2603
|
+
"trendPercent": -4.1,
|
|
2604
|
+
"description": "Product view to cart conversion rate is down YoY and pacing below target.",
|
|
2605
|
+
"ytdVariancePercent": -4.1,
|
|
2606
|
+
"priority": 3,
|
|
2607
|
+
"projectedValue": 0.7163,
|
|
2608
|
+
"dimensionValue": "",
|
|
2609
|
+
"metric": "PRODUCT_VIEW_TO_CART_CONVERSION_RATE",
|
|
2610
|
+
"variance": -0.0937,
|
|
2611
|
+
"trendDirection": "DOWN",
|
|
2612
|
+
"targetValue": 0.81,
|
|
2613
|
+
"category": "CONVERSION",
|
|
2614
|
+
"currentValue": 0.7187,
|
|
2615
|
+
"status": "AT_RISK"
|
|
2616
|
+
},
|
|
2617
|
+
{
|
|
2618
|
+
"priorYtdValue": 10.76,
|
|
2619
|
+
"pacingPercentage": -6.7,
|
|
2620
|
+
"trendPercent": -3.9,
|
|
2621
|
+
"description": "Cart to checkout conversion rate is down YoY and pacing 6.7% below target.",
|
|
2622
|
+
"ytdVariancePercent": -3.9,
|
|
2623
|
+
"priority": 2,
|
|
2624
|
+
"projectedValue": 10.32,
|
|
2625
|
+
"dimensionValue": "",
|
|
2626
|
+
"metric": "CART_TO_CHECKOUT_CONVERSION_RATE",
|
|
2627
|
+
"variance": -0.74,
|
|
2628
|
+
"trendDirection": "DOWN",
|
|
2629
|
+
"targetValue": 11.06,
|
|
2630
|
+
"category": "CONVERSION",
|
|
2631
|
+
"currentValue": 10.34,
|
|
2632
|
+
"status": "AT_RISK"
|
|
2633
|
+
},
|
|
2634
|
+
{
|
|
2635
|
+
"priorYtdValue": 179.45,
|
|
2636
|
+
"pacingPercentage": -28.8,
|
|
2637
|
+
"trendPercent": -25.2,
|
|
2638
|
+
"description": "Product view rate is down 25% YoY and pacing 29% below target.",
|
|
2639
|
+
"ytdVariancePercent": -25.2,
|
|
2640
|
+
"priority": 2,
|
|
2641
|
+
"projectedValue": 130.98,
|
|
2642
|
+
"dimensionValue": "",
|
|
2643
|
+
"metric": "PRODUCT_VIEW_RATE",
|
|
2644
|
+
"variance": -53.14,
|
|
2645
|
+
"trendDirection": "DOWN",
|
|
2646
|
+
"targetValue": 184.12,
|
|
2647
|
+
"category": "ENGAGEMENT",
|
|
2648
|
+
"currentValue": 134.26,
|
|
2649
|
+
"status": "AT_RISK"
|
|
2650
|
+
},
|
|
2651
|
+
{
|
|
2652
|
+
"priorYtdValue": 1.5158,
|
|
2653
|
+
"pacingPercentage": -19.8,
|
|
2654
|
+
"trendPercent": -15,
|
|
2655
|
+
"description": "Add to cart conversion rate is down 15% YoY and pacing 20% below target.",
|
|
2656
|
+
"ytdVariancePercent": -15,
|
|
2657
|
+
"priority": 2,
|
|
2658
|
+
"projectedValue": 1.275,
|
|
2659
|
+
"dimensionValue": "",
|
|
2660
|
+
"metric": "ADD_TO_CART_CONVERSION_RATE",
|
|
2661
|
+
"variance": -0.315,
|
|
2662
|
+
"trendDirection": "DOWN",
|
|
2663
|
+
"targetValue": 1.59,
|
|
2664
|
+
"category": "CONVERSION",
|
|
2665
|
+
"currentValue": 1.288,
|
|
2666
|
+
"status": "AT_RISK"
|
|
2667
|
+
},
|
|
2668
|
+
{
|
|
2669
|
+
"priorYtdValue": 14.09,
|
|
2670
|
+
"pacingPercentage": -18,
|
|
2671
|
+
"trendPercent": -11.6,
|
|
2672
|
+
"description": "Checkout conversion rate is down 11.6% YoY and pacing 18% below target.",
|
|
2673
|
+
"ytdVariancePercent": -11.6,
|
|
2674
|
+
"priority": 2,
|
|
2675
|
+
"projectedValue": 12.36,
|
|
2676
|
+
"dimensionValue": "",
|
|
2677
|
+
"metric": "CHECKOUT_CONVERSION_RATE",
|
|
2678
|
+
"variance": -2.71,
|
|
2679
|
+
"trendDirection": "DOWN",
|
|
2680
|
+
"targetValue": 15.07,
|
|
2681
|
+
"category": "CONVERSION",
|
|
2682
|
+
"currentValue": 12.46,
|
|
2683
|
+
"status": "AT_RISK"
|
|
2684
|
+
}
|
|
2685
|
+
]
|
|
2686
|
+
});
|
|
2687
|
+
|
|
2688
|
+
const _c0 = () => ({});
|
|
2689
|
+
function SymphiqFunnelAnalysisDashboardComponent_symphiq_funnel_analysis_insight_card_24_Template(rf, ctx) { if (rf & 1) {
|
|
2690
|
+
i0.ɵɵelement(0, "symphiq-funnel-analysis-insight-card", 32);
|
|
2691
|
+
} if (rf & 2) {
|
|
2692
|
+
const insight_r1 = ctx.$implicit;
|
|
2693
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
2694
|
+
i0.ɵɵproperty("insight", insight_r1)("allMetrics", ctx_r1.allMetrics);
|
|
2695
|
+
} }
|
|
2696
|
+
function SymphiqFunnelAnalysisDashboardComponent_option_31_Template(rf, ctx) { if (rf & 1) {
|
|
2697
|
+
i0.ɵɵelementStart(0, "option", 33);
|
|
2698
|
+
i0.ɵɵtext(1);
|
|
2699
|
+
i0.ɵɵelementEnd();
|
|
2700
|
+
} if (rf & 2) {
|
|
2701
|
+
const cat_r3 = ctx.$implicit;
|
|
2702
|
+
i0.ɵɵproperty("value", cat_r3.value);
|
|
2703
|
+
i0.ɵɵadvance();
|
|
2704
|
+
i0.ɵɵtextInterpolate(cat_r3.label);
|
|
2705
|
+
} }
|
|
2706
|
+
function SymphiqFunnelAnalysisDashboardComponent__svg_path_34_Template(rf, ctx) { if (rf & 1) {
|
|
2707
|
+
i0.ɵɵnamespaceSVG();
|
|
2708
|
+
i0.ɵɵelement(0, "path", 34);
|
|
2709
|
+
} }
|
|
2710
|
+
function SymphiqFunnelAnalysisDashboardComponent__svg_path_35_Template(rf, ctx) { if (rf & 1) {
|
|
2711
|
+
i0.ɵɵnamespaceSVG();
|
|
2712
|
+
i0.ɵɵelement(0, "path", 35);
|
|
2713
|
+
} }
|
|
2714
|
+
function SymphiqFunnelAnalysisDashboardComponent_ng_container_39_div_3_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
2715
|
+
i0.ɵɵelementStart(0, "div", 41);
|
|
2716
|
+
i0.ɵɵelement(1, "symphiq-funnel-analysis-metric-card", 42);
|
|
2717
|
+
i0.ɵɵelementEnd();
|
|
2718
|
+
} if (rf & 2) {
|
|
2719
|
+
const metric_r4 = ctx.$implicit;
|
|
2720
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
2721
|
+
i0.ɵɵadvance();
|
|
2722
|
+
i0.ɵɵproperty("metric", metric_r4)("insights", ctx_r1.insights);
|
|
2723
|
+
} }
|
|
2724
|
+
function SymphiqFunnelAnalysisDashboardComponent_ng_container_39_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
2725
|
+
i0.ɵɵelementStart(0, "div", 39);
|
|
2726
|
+
i0.ɵɵtemplate(1, SymphiqFunnelAnalysisDashboardComponent_ng_container_39_div_3_div_1_Template, 2, 2, "div", 40);
|
|
2727
|
+
i0.ɵɵelementEnd();
|
|
2728
|
+
} if (rf & 2) {
|
|
2729
|
+
const funnelGroup_r5 = i0.ɵɵnextContext().$implicit;
|
|
2730
|
+
i0.ɵɵadvance();
|
|
2731
|
+
i0.ɵɵproperty("ngForOf", funnelGroup_r5.relatedMetrics);
|
|
2732
|
+
} }
|
|
2733
|
+
function SymphiqFunnelAnalysisDashboardComponent_ng_container_39_Template(rf, ctx) { if (rf & 1) {
|
|
2734
|
+
i0.ɵɵelementContainerStart(0);
|
|
2735
|
+
i0.ɵɵelementStart(1, "div", 36);
|
|
2736
|
+
i0.ɵɵelement(2, "symphiq-funnel-analysis-metric-card", 37);
|
|
2737
|
+
i0.ɵɵelementEnd();
|
|
2738
|
+
i0.ɵɵtemplate(3, SymphiqFunnelAnalysisDashboardComponent_ng_container_39_div_3_Template, 2, 1, "div", 38);
|
|
2739
|
+
i0.ɵɵelementContainerEnd();
|
|
2740
|
+
} if (rf & 2) {
|
|
2741
|
+
const funnelGroup_r5 = ctx.$implicit;
|
|
2742
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
2743
|
+
i0.ɵɵadvance(2);
|
|
2744
|
+
i0.ɵɵproperty("metric", funnelGroup_r5.funnelMetric)("insights", ctx_r1.insights);
|
|
2745
|
+
i0.ɵɵadvance();
|
|
2746
|
+
i0.ɵɵproperty("ngIf", funnelGroup_r5.relatedMetrics.length > 0);
|
|
2747
|
+
} }
|
|
2748
|
+
function SymphiqFunnelAnalysisDashboardComponent_symphiq_funnel_analysis_breakdown_section_44_Template(rf, ctx) { if (rf & 1) {
|
|
2749
|
+
i0.ɵɵelement(0, "symphiq-funnel-analysis-breakdown-section", 43);
|
|
2750
|
+
} if (rf & 2) {
|
|
2751
|
+
const breakdown_r6 = ctx.$implicit;
|
|
2752
|
+
i0.ɵɵproperty("breakdown", breakdown_r6);
|
|
2753
|
+
} }
|
|
2754
|
+
class SymphiqFunnelAnalysisDashboardComponent {
|
|
2755
|
+
set data(value) {
|
|
2756
|
+
this._data = normalizeToV3(value);
|
|
2757
|
+
}
|
|
2758
|
+
get data() {
|
|
2759
|
+
return this._data;
|
|
2760
|
+
}
|
|
2761
|
+
constructor(funnelOrderService) {
|
|
2762
|
+
this.funnelOrderService = funnelOrderService;
|
|
2763
|
+
this._data = normalizeToV3(PERFORMANCE_DATA);
|
|
2764
|
+
this.insights = [];
|
|
2765
|
+
this.breakdowns = [];
|
|
2766
|
+
this.allMetrics = [];
|
|
2767
|
+
this.revenueMetric = undefined;
|
|
2768
|
+
this.categories = [
|
|
2769
|
+
{ value: 'ALL', label: 'All' },
|
|
2770
|
+
{ value: 'REVENUE', label: 'Revenue' },
|
|
2771
|
+
{ value: 'PURCHASES', label: 'Purchases' },
|
|
2772
|
+
{ value: 'CHECKOUTS', label: 'Checkouts' },
|
|
2773
|
+
{ value: 'ADD TO CARTS', label: 'Add to Carts' },
|
|
2774
|
+
{ value: 'PRODUCT VIEWS', label: 'Product Views' },
|
|
2775
|
+
{ value: 'VIEWS', label: 'Views' }
|
|
2776
|
+
];
|
|
2777
|
+
this.selectedCategory = 'ALL';
|
|
2778
|
+
this.reverseSortOrder = false;
|
|
2779
|
+
}
|
|
2780
|
+
ngOnInit() {
|
|
2781
|
+
this.initializeDerivedProperties();
|
|
2782
|
+
}
|
|
2783
|
+
initializeDerivedProperties() {
|
|
2784
|
+
this.insights = (this.data.insights || []).sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
2785
|
+
this.breakdowns = this.data.breakdowns || [];
|
|
2786
|
+
this.allMetrics = this.data.metrics || [];
|
|
2787
|
+
this.revenueMetric = (this.data.metrics || []).find(m => m.metric === 'PURCHASE_REVENUE');
|
|
2788
|
+
}
|
|
2789
|
+
getFilteredMetrics() {
|
|
2790
|
+
const metrics = this.data.metrics || [];
|
|
2791
|
+
let filteredMetrics = metrics;
|
|
2792
|
+
if (this.selectedCategory !== 'ALL') {
|
|
2793
|
+
filteredMetrics = this.funnelOrderService.getMetricsForFunnelStage(metrics, this.selectedCategory);
|
|
2794
|
+
}
|
|
2795
|
+
return this.funnelOrderService.sortMetricsByFunnelOrder(filteredMetrics);
|
|
2796
|
+
}
|
|
2797
|
+
getGroupedMetrics() {
|
|
2798
|
+
const sortedMetrics = this.getFilteredMetrics();
|
|
2799
|
+
const groups = [];
|
|
2800
|
+
let currentGroup = null;
|
|
2801
|
+
for (const metric of sortedMetrics) {
|
|
2802
|
+
if (this.isFunnelStage(metric.metric || '')) {
|
|
2803
|
+
if (currentGroup) {
|
|
2804
|
+
groups.push(currentGroup);
|
|
2805
|
+
}
|
|
2806
|
+
currentGroup = {
|
|
2807
|
+
funnelMetric: metric,
|
|
2808
|
+
relatedMetrics: []
|
|
2809
|
+
};
|
|
2810
|
+
}
|
|
2811
|
+
else if (currentGroup) {
|
|
2812
|
+
currentGroup.relatedMetrics.push(metric);
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
if (currentGroup) {
|
|
2816
|
+
groups.push(currentGroup);
|
|
2817
|
+
}
|
|
2818
|
+
return this.reverseSortOrder ? groups.reverse() : groups;
|
|
2819
|
+
}
|
|
2820
|
+
isFunnelStage(metricName) {
|
|
2821
|
+
return this.funnelOrderService.isFunnelStage(metricName);
|
|
2822
|
+
}
|
|
2823
|
+
formatDate(dateString) {
|
|
2824
|
+
return new Date(dateString).toLocaleDateString('en-US', {
|
|
2825
|
+
year: 'numeric',
|
|
2826
|
+
month: 'long',
|
|
2827
|
+
day: 'numeric',
|
|
2828
|
+
});
|
|
2829
|
+
}
|
|
2830
|
+
static { this.ɵfac = function SymphiqFunnelAnalysisDashboardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SymphiqFunnelAnalysisDashboardComponent)(i0.ɵɵdirectiveInject(FunnelOrderService)); }; }
|
|
2831
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SymphiqFunnelAnalysisDashboardComponent, selectors: [["symphiq-funnel-analysis-dashboard"]], inputs: { data: "data" }, decls: 53, vars: 13, consts: [[1, "min-h-screen", "bg-gradient-to-br", "from-slate-950", "via-slate-900", "to-slate-950"], [1, "bg-slate-900/50", "backdrop-blur-sm", "border-b", "border-slate-800", "sticky", "top-0", "z-50"], [1, "max-w-7xl", "mx-auto", "px-6", "py-6"], [1, "flex", "items-center", "justify-between"], [1, "text-3xl", "font-bold", "text-white", "mb-1"], [1, "text-slate-400"], [1, "text-right"], [1, "text-sm", "text-slate-500"], [1, "text-white", "font-medium"], [1, "max-w-7xl", "mx-auto", "px-6", "py-8"], [1, "space-y-8"], [3, "assessment", "revenueMetric"], [1, "flex", "items-center", "justify-between", "mb-6"], [1, "text-2xl", "font-bold", "text-white"], [1, "text-sm", "text-slate-400"], [1, "grid", "grid-cols-1", "lg:grid-cols-2", "gap-6"], [3, "insight", "allMetrics", 4, "ngFor", "ngForOf"], [1, "flex", "gap-3", "items-center"], [1, "px-4", "py-2", "rounded-lg", "text-sm", "font-medium", "bg-slate-700", "text-white", "border", "border-slate-600", "focus:outline-none", "focus:ring-2", "focus:ring-blue-500", "focus:border-transparent", "transition-all", "cursor-pointer", 3, "ngModelChange", "ngModel"], [3, "value", 4, "ngFor", "ngForOf"], [1, "px-4", "py-2", "rounded-lg", "text-sm", "font-medium", "bg-slate-700", "text-white", "border", "border-slate-600", "hover:bg-slate-600", "focus:outline-none", "focus:ring-2", "focus:ring-blue-500", "transition-all", "flex", "items-center", "gap-2", 3, "click", "title"], ["fill", "none", "stroke", "currentColor", "viewBox", "0 0 24 24", 1, "w-4", "h-4"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12", 4, "ngIf"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M3 4h13M3 8h9m-9 4h9m5-4v12m0 0l-4-4m4 4l4-4", 4, "ngIf"], [1, "hidden", "sm:inline"], [1, "space-y-6"], [4, "ngFor", "ngForOf"], [1, "text-2xl", "font-bold", "text-white", "mb-6"], [3, "breakdown", 4, "ngFor", "ngForOf"], [1, "bg-slate-900/50", "backdrop-blur-sm", "border-t", "border-slate-800", "mt-12"], [1, "text-center", "text-slate-500", "text-sm"], [1, "mt-1"], [3, "insight", "allMetrics"], [3, "value"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12"], ["stroke-linecap", "round", "stroke-linejoin", "round", "stroke-width", "2", "d", "M3 4h13M3 8h9m-9 4h9m5-4v12m0 0l-4-4m4 4l4-4"], [1, "w-full"], [3, "metric", "insights"], ["class", "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-stretch", 4, "ngIf"], [1, "grid", "grid-cols-1", "md:grid-cols-2", "lg:grid-cols-3", "gap-6", "items-stretch"], ["class", "flex", 4, "ngFor", "ngForOf"], [1, "flex"], [1, "flex-1", 3, "metric", "insights"], [3, "breakdown"]], template: function SymphiqFunnelAnalysisDashboardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
2832
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "header", 1)(2, "div", 2)(3, "div", 3)(4, "div")(5, "h1", 4);
|
|
2833
|
+
i0.ɵɵtext(6, "Symphiq Dashboard");
|
|
2834
|
+
i0.ɵɵelementEnd();
|
|
2835
|
+
i0.ɵɵelementStart(7, "p", 5);
|
|
2836
|
+
i0.ɵɵtext(8, "Revenue Orchestration & Funnel Analysis");
|
|
2837
|
+
i0.ɵɵelementEnd()();
|
|
2838
|
+
i0.ɵɵelementStart(9, "div", 6)(10, "div", 7);
|
|
2839
|
+
i0.ɵɵtext(11, "Generated At");
|
|
2840
|
+
i0.ɵɵelementEnd();
|
|
2841
|
+
i0.ɵɵelementStart(12, "div", 8);
|
|
2842
|
+
i0.ɵɵtext(13);
|
|
2843
|
+
i0.ɵɵelementEnd()()()()();
|
|
2844
|
+
i0.ɵɵelementStart(14, "main", 9)(15, "div", 10);
|
|
2845
|
+
i0.ɵɵelement(16, "symphiq-funnel-analysis-overall-assessment", 11);
|
|
2846
|
+
i0.ɵɵelementStart(17, "section")(18, "div", 12)(19, "h2", 13);
|
|
2847
|
+
i0.ɵɵtext(20, "Key Insights");
|
|
2848
|
+
i0.ɵɵelementEnd();
|
|
2849
|
+
i0.ɵɵelementStart(21, "span", 14);
|
|
2850
|
+
i0.ɵɵtext(22);
|
|
2851
|
+
i0.ɵɵelementEnd()();
|
|
2852
|
+
i0.ɵɵelementStart(23, "div", 15);
|
|
2853
|
+
i0.ɵɵtemplate(24, SymphiqFunnelAnalysisDashboardComponent_symphiq_funnel_analysis_insight_card_24_Template, 1, 2, "symphiq-funnel-analysis-insight-card", 16);
|
|
2854
|
+
i0.ɵɵelementEnd()();
|
|
2855
|
+
i0.ɵɵelementStart(25, "section")(26, "div", 12)(27, "h2", 13);
|
|
2856
|
+
i0.ɵɵtext(28, "Performance Metrics");
|
|
2857
|
+
i0.ɵɵelementEnd();
|
|
2858
|
+
i0.ɵɵelementStart(29, "div", 17)(30, "select", 18);
|
|
2859
|
+
i0.ɵɵtwoWayListener("ngModelChange", function SymphiqFunnelAnalysisDashboardComponent_Template_select_ngModelChange_30_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.selectedCategory, $event) || (ctx.selectedCategory = $event); return $event; });
|
|
2860
|
+
i0.ɵɵtemplate(31, SymphiqFunnelAnalysisDashboardComponent_option_31_Template, 2, 2, "option", 19);
|
|
2861
|
+
i0.ɵɵelementEnd();
|
|
2862
|
+
i0.ɵɵelementStart(32, "button", 20);
|
|
2863
|
+
i0.ɵɵlistener("click", function SymphiqFunnelAnalysisDashboardComponent_Template_button_click_32_listener() { return ctx.reverseSortOrder = !ctx.reverseSortOrder; });
|
|
2864
|
+
i0.ɵɵnamespaceSVG();
|
|
2865
|
+
i0.ɵɵelementStart(33, "svg", 21);
|
|
2866
|
+
i0.ɵɵtemplate(34, SymphiqFunnelAnalysisDashboardComponent__svg_path_34_Template, 1, 0, "path", 22)(35, SymphiqFunnelAnalysisDashboardComponent__svg_path_35_Template, 1, 0, "path", 23);
|
|
2867
|
+
i0.ɵɵelementEnd();
|
|
2868
|
+
i0.ɵɵnamespaceHTML();
|
|
2869
|
+
i0.ɵɵelementStart(36, "span", 24);
|
|
2870
|
+
i0.ɵɵtext(37, "Sort");
|
|
2871
|
+
i0.ɵɵelementEnd()()()();
|
|
2872
|
+
i0.ɵɵelementStart(38, "div", 25);
|
|
2873
|
+
i0.ɵɵtemplate(39, SymphiqFunnelAnalysisDashboardComponent_ng_container_39_Template, 4, 3, "ng-container", 26);
|
|
2874
|
+
i0.ɵɵelementEnd()();
|
|
2875
|
+
i0.ɵɵelementStart(40, "section")(41, "h2", 27);
|
|
2876
|
+
i0.ɵɵtext(42, "Performance Breakdowns");
|
|
2877
|
+
i0.ɵɵelementEnd();
|
|
2878
|
+
i0.ɵɵelementStart(43, "div");
|
|
2879
|
+
i0.ɵɵtemplate(44, SymphiqFunnelAnalysisDashboardComponent_symphiq_funnel_analysis_breakdown_section_44_Template, 1, 1, "symphiq-funnel-analysis-breakdown-section", 28);
|
|
2880
|
+
i0.ɵɵelementEnd()()()();
|
|
2881
|
+
i0.ɵɵelement(45, "symphiq-funnel-analysis-modal");
|
|
2882
|
+
i0.ɵɵelementStart(46, "footer", 29)(47, "div", 9)(48, "div", 30)(49, "p");
|
|
2883
|
+
i0.ɵɵtext(50, "Symphiq Revenue Orchestration Workspace");
|
|
2884
|
+
i0.ɵɵelementEnd();
|
|
2885
|
+
i0.ɵɵelementStart(51, "p", 31);
|
|
2886
|
+
i0.ɵɵtext(52, "\u00A9 2025 Symphiq. All rights reserved.");
|
|
2887
|
+
i0.ɵɵelementEnd()()()()();
|
|
2888
|
+
} if (rf & 2) {
|
|
2889
|
+
i0.ɵɵadvance(13);
|
|
2890
|
+
i0.ɵɵtextInterpolate(ctx.formatDate(ctx.data.generatedAt || "2025-11-30"));
|
|
2891
|
+
i0.ɵɵadvance(3);
|
|
2892
|
+
i0.ɵɵproperty("assessment", ctx.data.overallAssessment || i0.ɵɵpureFunction0(12, _c0))("revenueMetric", ctx.revenueMetric);
|
|
2893
|
+
i0.ɵɵadvance(6);
|
|
2894
|
+
i0.ɵɵtextInterpolate1("", ctx.insights.length, " insights");
|
|
2895
|
+
i0.ɵɵadvance(2);
|
|
2896
|
+
i0.ɵɵproperty("ngForOf", ctx.insights);
|
|
2897
|
+
i0.ɵɵadvance(6);
|
|
2898
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.selectedCategory);
|
|
2899
|
+
i0.ɵɵadvance();
|
|
2900
|
+
i0.ɵɵproperty("ngForOf", ctx.categories);
|
|
2901
|
+
i0.ɵɵadvance();
|
|
2902
|
+
i0.ɵɵproperty("title", ctx.reverseSortOrder ? "Sort: Revenue to Views" : "Sort: Views to Revenue");
|
|
2903
|
+
i0.ɵɵadvance(2);
|
|
2904
|
+
i0.ɵɵproperty("ngIf", !ctx.reverseSortOrder);
|
|
2905
|
+
i0.ɵɵadvance();
|
|
2906
|
+
i0.ɵɵproperty("ngIf", ctx.reverseSortOrder);
|
|
2907
|
+
i0.ɵɵadvance(4);
|
|
2908
|
+
i0.ɵɵproperty("ngForOf", ctx.getGroupedMetrics());
|
|
2909
|
+
i0.ɵɵadvance(5);
|
|
2910
|
+
i0.ɵɵproperty("ngForOf", ctx.breakdowns);
|
|
2911
|
+
} }, dependencies: [CommonModule, i2.NgForOf, i2.NgIf, FormsModule, i3.NgSelectOption, i3.ɵNgSelectMultipleOption, i3.SelectControlValueAccessor, i3.NgControlStatus, i3.NgModel, OverallAssessmentComponent,
|
|
2912
|
+
InsightCardComponent,
|
|
2913
|
+
MetricCardComponent,
|
|
2914
|
+
BreakdownSectionComponent,
|
|
2915
|
+
ModalComponent], encapsulation: 2 }); }
|
|
2916
|
+
}
|
|
2917
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SymphiqFunnelAnalysisDashboardComponent, [{
|
|
2918
|
+
type: Component,
|
|
2919
|
+
args: [{
|
|
2920
|
+
selector: 'symphiq-funnel-analysis-dashboard',
|
|
2921
|
+
standalone: true,
|
|
2922
|
+
imports: [
|
|
2923
|
+
CommonModule,
|
|
2924
|
+
FormsModule,
|
|
2925
|
+
OverallAssessmentComponent,
|
|
2926
|
+
InsightCardComponent,
|
|
2927
|
+
MetricCardComponent,
|
|
2928
|
+
BreakdownSectionComponent,
|
|
2929
|
+
ModalComponent,
|
|
2930
|
+
],
|
|
2931
|
+
template: `
|
|
2932
|
+
<div class="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950">
|
|
2933
|
+
<header class="bg-slate-900/50 backdrop-blur-sm border-b border-slate-800 sticky top-0 z-50">
|
|
2934
|
+
<div class="max-w-7xl mx-auto px-6 py-6">
|
|
2935
|
+
<div class="flex items-center justify-between">
|
|
2936
|
+
<div>
|
|
2937
|
+
<h1 class="text-3xl font-bold text-white mb-1">Symphiq Dashboard</h1>
|
|
2938
|
+
<p class="text-slate-400">Revenue Orchestration & Funnel Analysis</p>
|
|
2939
|
+
</div>
|
|
2940
|
+
<div class="text-right">
|
|
2941
|
+
<div class="text-sm text-slate-500">Generated At</div>
|
|
2942
|
+
<div class="text-white font-medium">{{ formatDate(data.generatedAt || '2025-11-30') }}</div>
|
|
2943
|
+
</div>
|
|
2944
|
+
</div>
|
|
2945
|
+
</div>
|
|
2946
|
+
</header>
|
|
2947
|
+
|
|
2948
|
+
<main class="max-w-7xl mx-auto px-6 py-8">
|
|
2949
|
+
<div class="space-y-8">
|
|
2950
|
+
<symphiq-funnel-analysis-overall-assessment
|
|
2951
|
+
[assessment]="data.overallAssessment || {}"
|
|
2952
|
+
[revenueMetric]="revenueMetric" />
|
|
2953
|
+
|
|
2954
|
+
<section>
|
|
2955
|
+
<div class="flex items-center justify-between mb-6">
|
|
2956
|
+
<h2 class="text-2xl font-bold text-white">Key Insights</h2>
|
|
2957
|
+
<span class="text-sm text-slate-400">{{ insights.length }} insights</span>
|
|
2958
|
+
</div>
|
|
2959
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
2960
|
+
<symphiq-funnel-analysis-insight-card *ngFor="let insight of insights" [insight]="insight" [allMetrics]="allMetrics" />
|
|
2961
|
+
</div>
|
|
2962
|
+
</section>
|
|
2963
|
+
|
|
2964
|
+
<section>
|
|
2965
|
+
<div class="flex items-center justify-between mb-6">
|
|
2966
|
+
<h2 class="text-2xl font-bold text-white">Performance Metrics</h2>
|
|
2967
|
+
<div class="flex gap-3 items-center">
|
|
2968
|
+
<select
|
|
2969
|
+
[(ngModel)]="selectedCategory"
|
|
2970
|
+
class="px-4 py-2 rounded-lg text-sm font-medium bg-slate-700 text-white border border-slate-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all cursor-pointer">
|
|
2971
|
+
<option *ngFor="let cat of categories" [value]="cat.value">{{ cat.label }}</option>
|
|
2972
|
+
</select>
|
|
2973
|
+
<button
|
|
2974
|
+
(click)="reverseSortOrder = !reverseSortOrder"
|
|
2975
|
+
class="px-4 py-2 rounded-lg text-sm font-medium bg-slate-700 text-white border border-slate-600 hover:bg-slate-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all flex items-center gap-2"
|
|
2976
|
+
[title]="reverseSortOrder ? 'Sort: Revenue to Views' : 'Sort: Views to Revenue'">
|
|
2977
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
2978
|
+
<path *ngIf="!reverseSortOrder" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12"></path>
|
|
2979
|
+
<path *ngIf="reverseSortOrder" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4h13M3 8h9m-9 4h9m5-4v12m0 0l-4-4m4 4l4-4"></path>
|
|
2980
|
+
</svg>
|
|
2981
|
+
<span class="hidden sm:inline">Sort</span>
|
|
2982
|
+
</button>
|
|
2983
|
+
</div>
|
|
2984
|
+
</div>
|
|
2985
|
+
<div class="space-y-6">
|
|
2986
|
+
<ng-container *ngFor="let funnelGroup of getGroupedMetrics()">
|
|
2987
|
+
<div class="w-full">
|
|
2988
|
+
<symphiq-funnel-analysis-metric-card [metric]="funnelGroup.funnelMetric" [insights]="insights" />
|
|
2989
|
+
</div>
|
|
2990
|
+
<div *ngIf="funnelGroup.relatedMetrics.length > 0" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-stretch">
|
|
2991
|
+
<div *ngFor="let metric of funnelGroup.relatedMetrics" class="flex">
|
|
2992
|
+
<symphiq-funnel-analysis-metric-card class="flex-1" [metric]="metric" [insights]="insights" />
|
|
2993
|
+
</div>
|
|
2994
|
+
</div>
|
|
2995
|
+
</ng-container>
|
|
2996
|
+
</div>
|
|
2997
|
+
</section>
|
|
2998
|
+
|
|
2999
|
+
<section>
|
|
3000
|
+
<h2 class="text-2xl font-bold text-white mb-6">Performance Breakdowns</h2>
|
|
3001
|
+
<div>
|
|
3002
|
+
<symphiq-funnel-analysis-breakdown-section *ngFor="let breakdown of breakdowns" [breakdown]="breakdown" />
|
|
3003
|
+
</div>
|
|
3004
|
+
</section>
|
|
3005
|
+
</div>
|
|
3006
|
+
</main>
|
|
3007
|
+
|
|
3008
|
+
<symphiq-funnel-analysis-modal></symphiq-funnel-analysis-modal>
|
|
3009
|
+
|
|
3010
|
+
<footer class="bg-slate-900/50 backdrop-blur-sm border-t border-slate-800 mt-12">
|
|
3011
|
+
<div class="max-w-7xl mx-auto px-6 py-8">
|
|
3012
|
+
<div class="text-center text-slate-500 text-sm">
|
|
3013
|
+
<p>Symphiq Revenue Orchestration Workspace</p>
|
|
3014
|
+
<p class="mt-1">© 2025 Symphiq. All rights reserved.</p>
|
|
3015
|
+
</div>
|
|
3016
|
+
</div>
|
|
3017
|
+
</footer>
|
|
3018
|
+
</div>
|
|
3019
|
+
`,
|
|
3020
|
+
}]
|
|
3021
|
+
}], () => [{ type: FunnelOrderService }], { data: [{
|
|
3022
|
+
type: Input
|
|
3023
|
+
}] }); })();
|
|
3024
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SymphiqFunnelAnalysisDashboardComponent, { className: "SymphiqFunnelAnalysisDashboardComponent", filePath: "lib/components/symphiq-funnel-analysis-dashboard.component.ts", lineNumber: 115 }); })();
|
|
3025
|
+
|
|
3026
|
+
/*
|
|
3027
|
+
* Public API Surface of symphiq-components
|
|
3028
|
+
*/
|
|
3029
|
+
// Export all models from @jebgem/model
|
|
3030
|
+
|
|
3031
|
+
/**
|
|
3032
|
+
* Generated bundle index. Do not edit.
|
|
3033
|
+
*/
|
|
3034
|
+
|
|
3035
|
+
export { BreakdownSectionComponent, FunnelOrderService, InsightCardComponent, MetricCardComponent, ModalComponent, ModalService, OverallAssessmentComponent, PERFORMANCE_DATA, SymphiqFunnelAnalysisDashboardComponent };
|
|
3036
|
+
//# sourceMappingURL=symphiq-components.mjs.map
|