@iann29/rastro 0.9.0 → 0.10.1

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.
Files changed (265) hide show
  1. package/README.md +9 -1
  2. package/agent/integration.md +23 -1
  3. package/agent/manifest.json +4 -4
  4. package/dist/client/federation.d.ts +114 -9
  5. package/dist/client/federation.d.ts.map +1 -1
  6. package/dist/client/federation.js +54 -2
  7. package/dist/client/federation.js.map +1 -1
  8. package/dist/client/index.d.ts +642 -8
  9. package/dist/client/index.d.ts.map +1 -1
  10. package/dist/client/index.js +391 -9
  11. package/dist/client/index.js.map +1 -1
  12. package/dist/component/_generated/api.d.ts +14 -0
  13. package/dist/component/_generated/api.d.ts.map +1 -1
  14. package/dist/component/_generated/api.js.map +1 -1
  15. package/dist/component/_generated/component.d.ts +278 -1
  16. package/dist/component/_generated/component.d.ts.map +1 -1
  17. package/dist/component/_generated/server.d.ts +4 -0
  18. package/dist/component/_generated/server.d.ts.map +1 -1
  19. package/dist/component/_generated/server.js.map +1 -1
  20. package/dist/component/constants.d.ts +5 -0
  21. package/dist/component/constants.d.ts.map +1 -1
  22. package/dist/component/constants.js +10 -0
  23. package/dist/component/constants.js.map +1 -1
  24. package/dist/component/convex.config.d.ts +4 -0
  25. package/dist/component/convex.config.js +9 -0
  26. package/dist/component/convex.config.js.map +1 -1
  27. package/dist/component/errors.d.ts +1 -1
  28. package/dist/component/errors.d.ts.map +1 -1
  29. package/dist/component/errors.js.map +1 -1
  30. package/dist/component/leads.d.ts +69 -3
  31. package/dist/component/leads.d.ts.map +1 -1
  32. package/dist/component/leads.js +419 -16
  33. package/dist/component/leads.js.map +1 -1
  34. package/dist/component/meta.d.ts +358 -0
  35. package/dist/component/meta.d.ts.map +1 -0
  36. package/dist/component/meta.js +1295 -0
  37. package/dist/component/meta.js.map +1 -0
  38. package/dist/component/metaCapi.d.ts +162 -0
  39. package/dist/component/metaCapi.d.ts.map +1 -0
  40. package/dist/component/metaCapi.js +754 -0
  41. package/dist/component/metaCapi.js.map +1 -0
  42. package/dist/component/metaFake.d.ts +5 -0
  43. package/dist/component/metaFake.d.ts.map +1 -0
  44. package/dist/component/metaFake.js +225 -0
  45. package/dist/component/metaFake.js.map +1 -0
  46. package/dist/component/metaGraph.d.ts +165 -0
  47. package/dist/component/metaGraph.d.ts.map +1 -0
  48. package/dist/component/metaGraph.js +507 -0
  49. package/dist/component/metaGraph.js.map +1 -0
  50. package/dist/component/metaSync.d.ts +186 -0
  51. package/dist/component/metaSync.d.ts.map +1 -0
  52. package/dist/component/metaSync.js +961 -0
  53. package/dist/component/metaSync.js.map +1 -0
  54. package/dist/component/pipeline.d.ts +99 -0
  55. package/dist/component/pipeline.d.ts.map +1 -0
  56. package/dist/component/pipeline.js +337 -0
  57. package/dist/component/pipeline.js.map +1 -0
  58. package/dist/component/schema.d.ts +404 -2
  59. package/dist/component/schema.d.ts.map +1 -1
  60. package/dist/component/schema.js +168 -2
  61. package/dist/component/schema.js.map +1 -1
  62. package/dist/component/secrets.d.ts +15 -0
  63. package/dist/component/secrets.d.ts.map +1 -0
  64. package/dist/component/secrets.js +67 -0
  65. package/dist/component/secrets.js.map +1 -0
  66. package/dist/component/validators.d.ts +1052 -15
  67. package/dist/component/validators.d.ts.map +1 -1
  68. package/dist/component/validators.js +307 -3
  69. package/dist/component/validators.js.map +1 -1
  70. package/dist/tracker/generated.d.ts +1 -1
  71. package/dist/tracker/generated.d.ts.map +1 -1
  72. package/dist/tracker/generated.js +1 -1
  73. package/dist/tracker/generated.js.map +1 -1
  74. package/dist/ui/campaigns/CampaignsView.d.ts +48 -0
  75. package/dist/ui/campaigns/CampaignsView.d.ts.map +1 -0
  76. package/dist/ui/campaigns/CampaignsView.js +47 -0
  77. package/dist/ui/campaigns/CampaignsView.js.map +1 -0
  78. package/dist/ui/campaigns/Conversions.d.ts +16 -0
  79. package/dist/ui/campaigns/Conversions.d.ts.map +1 -0
  80. package/dist/ui/campaigns/Conversions.js +40 -0
  81. package/dist/ui/campaigns/Conversions.js.map +1 -0
  82. package/dist/ui/campaigns/Drawer.d.ts +24 -0
  83. package/dist/ui/campaigns/Drawer.d.ts.map +1 -0
  84. package/dist/ui/campaigns/Drawer.js +120 -0
  85. package/dist/ui/campaigns/Drawer.js.map +1 -0
  86. package/dist/ui/campaigns/Icons.d.ts +35 -0
  87. package/dist/ui/campaigns/Icons.d.ts.map +1 -0
  88. package/dist/ui/campaigns/Icons.js +41 -0
  89. package/dist/ui/campaigns/Icons.js.map +1 -0
  90. package/dist/ui/campaigns/Summary.d.ts +21 -0
  91. package/dist/ui/campaigns/Summary.d.ts.map +1 -0
  92. package/dist/ui/campaigns/Summary.js +56 -0
  93. package/dist/ui/campaigns/Summary.js.map +1 -0
  94. package/dist/ui/campaigns/Thumbnail.d.ts +11 -0
  95. package/dist/ui/campaigns/Thumbnail.d.ts.map +1 -0
  96. package/dist/ui/campaigns/Thumbnail.js +23 -0
  97. package/dist/ui/campaigns/Thumbnail.js.map +1 -0
  98. package/dist/ui/campaigns/Tree.d.ts +12 -0
  99. package/dist/ui/campaigns/Tree.d.ts.map +1 -0
  100. package/dist/ui/campaigns/Tree.js +105 -0
  101. package/dist/ui/campaigns/Tree.js.map +1 -0
  102. package/dist/ui/campaigns/glyphs.d.ts +20 -0
  103. package/dist/ui/campaigns/glyphs.d.ts.map +1 -0
  104. package/dist/ui/campaigns/glyphs.js +19 -0
  105. package/dist/ui/campaigns/glyphs.js.map +1 -0
  106. package/dist/ui/campaigns/index.d.ts +4 -0
  107. package/dist/ui/campaigns/index.d.ts.map +1 -0
  108. package/dist/ui/campaigns/index.js +3 -0
  109. package/dist/ui/campaigns/index.js.map +1 -0
  110. package/dist/ui/campaigns/labels.d.ts +53 -0
  111. package/dist/ui/campaigns/labels.d.ts.map +1 -0
  112. package/dist/ui/campaigns/labels.js +169 -0
  113. package/dist/ui/campaigns/labels.js.map +1 -0
  114. package/dist/ui/campaigns/model.d.ts +99 -0
  115. package/dist/ui/campaigns/model.d.ts.map +1 -0
  116. package/dist/ui/campaigns/model.js +230 -0
  117. package/dist/ui/campaigns/model.js.map +1 -0
  118. package/dist/ui/campaigns/modes.d.ts +18 -0
  119. package/dist/ui/campaigns/modes.d.ts.map +1 -0
  120. package/dist/ui/campaigns/modes.js +7 -0
  121. package/dist/ui/campaigns/modes.js.map +1 -0
  122. package/dist/ui/index.d.ts +21 -0
  123. package/dist/ui/index.d.ts.map +1 -0
  124. package/dist/ui/index.js +20 -0
  125. package/dist/ui/index.js.map +1 -0
  126. package/dist/ui/integrations/AccountPicker.d.ts +26 -0
  127. package/dist/ui/integrations/AccountPicker.d.ts.map +1 -0
  128. package/dist/ui/integrations/AccountPicker.js +101 -0
  129. package/dist/ui/integrations/AccountPicker.js.map +1 -0
  130. package/dist/ui/integrations/ClientCards.d.ts +17 -0
  131. package/dist/ui/integrations/ClientCards.d.ts.map +1 -0
  132. package/dist/ui/integrations/ClientCards.js +42 -0
  133. package/dist/ui/integrations/ClientCards.js.map +1 -0
  134. package/dist/ui/integrations/ClientsTable.d.ts +36 -0
  135. package/dist/ui/integrations/ClientsTable.d.ts.map +1 -0
  136. package/dist/ui/integrations/ClientsTable.js +143 -0
  137. package/dist/ui/integrations/ClientsTable.js.map +1 -0
  138. package/dist/ui/integrations/Connect.d.ts +35 -0
  139. package/dist/ui/integrations/Connect.d.ts.map +1 -0
  140. package/dist/ui/integrations/Connect.js +62 -0
  141. package/dist/ui/integrations/Connect.js.map +1 -0
  142. package/dist/ui/integrations/Connection.d.ts +37 -0
  143. package/dist/ui/integrations/Connection.d.ts.map +1 -0
  144. package/dist/ui/integrations/Connection.js +108 -0
  145. package/dist/ui/integrations/Connection.js.map +1 -0
  146. package/dist/ui/integrations/Dialogs.d.ts +21 -0
  147. package/dist/ui/integrations/Dialogs.d.ts.map +1 -0
  148. package/dist/ui/integrations/Dialogs.js +34 -0
  149. package/dist/ui/integrations/Dialogs.js.map +1 -0
  150. package/dist/ui/integrations/EventsFeed.d.ts +15 -0
  151. package/dist/ui/integrations/EventsFeed.d.ts.map +1 -0
  152. package/dist/ui/integrations/EventsFeed.js +61 -0
  153. package/dist/ui/integrations/EventsFeed.js.map +1 -0
  154. package/dist/ui/integrations/EventsSheet.d.ts +34 -0
  155. package/dist/ui/integrations/EventsSheet.d.ts.map +1 -0
  156. package/dist/ui/integrations/EventsSheet.js +136 -0
  157. package/dist/ui/integrations/EventsSheet.js.map +1 -0
  158. package/dist/ui/integrations/Icons.d.ts +38 -0
  159. package/dist/ui/integrations/Icons.d.ts.map +1 -0
  160. package/dist/ui/integrations/Icons.js +40 -0
  161. package/dist/ui/integrations/Icons.js.map +1 -0
  162. package/dist/ui/integrations/IntegrationsView.d.ts +15 -0
  163. package/dist/ui/integrations/IntegrationsView.d.ts.map +1 -0
  164. package/dist/ui/integrations/IntegrationsView.js +141 -0
  165. package/dist/ui/integrations/IntegrationsView.js.map +1 -0
  166. package/dist/ui/integrations/Overlay.d.ts +18 -0
  167. package/dist/ui/integrations/Overlay.d.ts.map +1 -0
  168. package/dist/ui/integrations/Overlay.js +36 -0
  169. package/dist/ui/integrations/Overlay.js.map +1 -0
  170. package/dist/ui/integrations/Partner.d.ts +14 -0
  171. package/dist/ui/integrations/Partner.d.ts.map +1 -0
  172. package/dist/ui/integrations/Partner.js +38 -0
  173. package/dist/ui/integrations/Partner.js.map +1 -0
  174. package/dist/ui/integrations/RowMenu.d.ts +15 -0
  175. package/dist/ui/integrations/RowMenu.d.ts.map +1 -0
  176. package/dist/ui/integrations/RowMenu.js +47 -0
  177. package/dist/ui/integrations/RowMenu.js.map +1 -0
  178. package/dist/ui/integrations/UnlinkDialog.d.ts +18 -0
  179. package/dist/ui/integrations/UnlinkDialog.d.ts.map +1 -0
  180. package/dist/ui/integrations/UnlinkDialog.js +29 -0
  181. package/dist/ui/integrations/UnlinkDialog.js.map +1 -0
  182. package/dist/ui/integrations/audience.d.ts +12 -0
  183. package/dist/ui/integrations/audience.d.ts.map +1 -0
  184. package/dist/ui/integrations/audience.js +12 -0
  185. package/dist/ui/integrations/audience.js.map +1 -0
  186. package/dist/ui/integrations/format.d.ts +28 -0
  187. package/dist/ui/integrations/format.d.ts.map +1 -0
  188. package/dist/ui/integrations/format.js +101 -0
  189. package/dist/ui/integrations/format.js.map +1 -0
  190. package/dist/ui/integrations/index.d.ts +5 -0
  191. package/dist/ui/integrations/index.d.ts.map +1 -0
  192. package/dist/ui/integrations/index.js +3 -0
  193. package/dist/ui/integrations/index.js.map +1 -0
  194. package/dist/ui/integrations/modal.d.ts +10 -0
  195. package/dist/ui/integrations/modal.d.ts.map +1 -0
  196. package/dist/ui/integrations/modal.js +77 -0
  197. package/dist/ui/integrations/modal.js.map +1 -0
  198. package/dist/ui/integrations/model.d.ts +99 -0
  199. package/dist/ui/integrations/model.d.ts.map +1 -0
  200. package/dist/ui/integrations/model.js +164 -0
  201. package/dist/ui/integrations/model.js.map +1 -0
  202. package/dist/ui/integrations/parts.d.ts +36 -0
  203. package/dist/ui/integrations/parts.d.ts.map +1 -0
  204. package/dist/ui/integrations/parts.js +85 -0
  205. package/dist/ui/integrations/parts.js.map +1 -0
  206. package/dist/ui/integrations/types.d.ts +146 -0
  207. package/dist/ui/integrations/types.d.ts.map +1 -0
  208. package/dist/ui/integrations/types.js +2 -0
  209. package/dist/ui/integrations/types.js.map +1 -0
  210. package/dist/ui/metrics.d.ts +106 -0
  211. package/dist/ui/metrics.d.ts.map +1 -0
  212. package/dist/ui/metrics.js +166 -0
  213. package/dist/ui/metrics.js.map +1 -0
  214. package/dist/ui/portfolio/PortfolioView.d.ts +26 -0
  215. package/dist/ui/portfolio/PortfolioView.d.ts.map +1 -0
  216. package/dist/ui/portfolio/PortfolioView.js +216 -0
  217. package/dist/ui/portfolio/PortfolioView.js.map +1 -0
  218. package/dist/ui/portfolio/SpendChart.d.ts +10 -0
  219. package/dist/ui/portfolio/SpendChart.d.ts.map +1 -0
  220. package/dist/ui/portfolio/SpendChart.js +196 -0
  221. package/dist/ui/portfolio/SpendChart.js.map +1 -0
  222. package/dist/ui/portfolio/alerts.d.ts +74 -0
  223. package/dist/ui/portfolio/alerts.d.ts.map +1 -0
  224. package/dist/ui/portfolio/alerts.js +291 -0
  225. package/dist/ui/portfolio/alerts.js.map +1 -0
  226. package/dist/ui/portfolio/figures.d.ts +79 -0
  227. package/dist/ui/portfolio/figures.d.ts.map +1 -0
  228. package/dist/ui/portfolio/figures.js +103 -0
  229. package/dist/ui/portfolio/figures.js.map +1 -0
  230. package/dist/ui/portfolio/format.d.ts +8 -0
  231. package/dist/ui/portfolio/format.d.ts.map +1 -0
  232. package/dist/ui/portfolio/format.js +28 -0
  233. package/dist/ui/portfolio/format.js.map +1 -0
  234. package/dist/ui/sample.d.ts +536 -0
  235. package/dist/ui/sample.d.ts.map +1 -0
  236. package/dist/ui/sample.js +574 -0
  237. package/dist/ui/sample.js.map +1 -0
  238. package/dist/ui/seed.d.ts +188 -0
  239. package/dist/ui/seed.d.ts.map +1 -0
  240. package/dist/ui/seed.js +563 -0
  241. package/dist/ui/seed.js.map +1 -0
  242. package/dist/ui/ui.css +3424 -0
  243. package/docs/federation.md +46 -1
  244. package/docs/meta-ads.md +795 -0
  245. package/docs/upgrading.md +116 -0
  246. package/llms.txt +3 -0
  247. package/package.json +15 -3
  248. package/src/component/_generated/api.ts +14 -0
  249. package/src/component/_generated/component.ts +324 -1
  250. package/src/component/_generated/server.ts +4 -0
  251. package/src/component/constants.ts +10 -0
  252. package/src/component/convex.config.ts +9 -0
  253. package/src/component/errors.ts +5 -1
  254. package/src/component/leads.ts +517 -17
  255. package/src/component/meta.ts +1525 -0
  256. package/src/component/metaCapi.ts +921 -0
  257. package/src/component/metaFake.ts +277 -0
  258. package/src/component/metaGraph.ts +722 -0
  259. package/src/component/metaSync.ts +1164 -0
  260. package/src/component/pipeline.ts +410 -0
  261. package/src/component/schema.ts +197 -1
  262. package/src/component/secrets.ts +98 -0
  263. package/src/component/validators.ts +376 -3
  264. package/src/tracker/generated.ts +1 -1
  265. package/src/ui/seed.ts +755 -0
@@ -0,0 +1,196 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { formatMoney, formatReturn, RETURN_BAND_LABELS, returnBand, RETURN_BANDS, } from "../metrics.js";
4
+ import { spendState, } from "./figures.js";
5
+ import { compactMoney } from "./format.js";
6
+ /** The chart draws at its real width, so labels keep their size on a phone. */
7
+ function useWidth(fallback) {
8
+ const ref = useRef(null);
9
+ const [width, setWidth] = useState(fallback);
10
+ useEffect(() => {
11
+ const element = ref.current;
12
+ if (!element || typeof ResizeObserver === "undefined")
13
+ return;
14
+ const observer = new ResizeObserver(([entry]) => {
15
+ const next = Math.round(entry?.contentRect.width ?? 0);
16
+ if (next > 0)
17
+ setWidth(next);
18
+ });
19
+ observer.observe(element);
20
+ return () => observer.disconnect();
21
+ }, []);
22
+ return [ref, width];
23
+ }
24
+ /** Round axis steps: 1, 2, 2.5 or 5 times a power of ten. */
25
+ function niceTicks(max, count) {
26
+ const raw = max / count;
27
+ const power = 10 ** Math.floor(Math.log10(raw));
28
+ const step = [1, 2, 2.5, 5, 10].map((factor) => factor * power).find((s) => s >= raw) ??
29
+ raw;
30
+ const top = Math.ceil(max / step) * step;
31
+ return Array.from({ length: Math.round(top / step) + 1 }, (_, i) => i * step);
32
+ }
33
+ const overlaps = (a, b) => a.x < b.x + b.width &&
34
+ b.x < a.x + a.width &&
35
+ a.y < b.y + b.height &&
36
+ b.y < a.y + a.height;
37
+ const LABEL_HEIGHT = 16;
38
+ /** A long client name shortened for the plot; the bubble's title keeps it whole. */
39
+ const shortName = (name, max) => name.length > max ? `${name.slice(0, max - 1).trimEnd()}…` : name;
40
+ /** A rough label width: 7px a character plus the value beside the name. */
41
+ const labelWidth = (name, value) => (name.length + value.length) * 7 + 8;
42
+ /** Return ticks on the band edges: 0, 4, 8, 12, 16… up to the highest point. */
43
+ function returnTicks(max) {
44
+ const step = RETURN_BANDS.ok;
45
+ const top = Math.max(RETURN_BANDS.strong * 2, Math.ceil((max * 1.05) / step) * step);
46
+ return Array.from({ length: top / step + 1 }, (_, i) => i * step);
47
+ }
48
+ /** Spend against return (or cost per sale), one bubble per client. */
49
+ export function SpendChart({ portfolio, spendCurrency, mode, }) {
50
+ const [ref, width] = useWidth(640);
51
+ const narrow = width < 480;
52
+ const height = Math.round(Math.min(340, Math.max(240, width * 0.46)));
53
+ const pad = { left: narrow ? 40 : 52, right: 12, top: 28, bottom: 26 };
54
+ const plotWidth = width - pad.left - pad.right;
55
+ const plotHeight = height - pad.top - pad.bottom;
56
+ const valueOf = (client) => mode === "return"
57
+ ? client.metrics.returnOnSpend
58
+ : client.metrics.costPerSaleCents;
59
+ // Only the clients the totals count: their spend shares one currency.
60
+ const points = portfolio.clients.filter((client) => spendState(client, portfolio) === "counted" && valueOf(client) !== null);
61
+ const format = (value) => mode === "return" ? formatReturn(value) : formatMoney(value, spendCurrency);
62
+ const xTicks = niceTicks(Math.max(...points.map((p) => p.spendCents), 100) * 1.08, narrow ? 3 : 5);
63
+ const yMaxValue = Math.max(...points.map((p) => valueOf(p)), 0);
64
+ const yTicks = mode === "return"
65
+ ? returnTicks(yMaxValue)
66
+ : niceTicks(Math.max(yMaxValue, 100) * 1.08, 4);
67
+ const xMax = xTicks.at(-1);
68
+ const yMax = yTicks.at(-1);
69
+ const sx = (value) => pad.left + (value / xMax) * plotWidth;
70
+ const sy = (value) => pad.top + (1 - value / yMax) * plotHeight;
71
+ const maxRevenue = Math.max(...points.map((p) => p.revenueCents), 1);
72
+ const radius = (revenueCents) => (narrow ? 5 : 7) +
73
+ Math.sqrt(revenueCents / maxRevenue) * (narrow ? 11 : 17);
74
+ const average = mode === "return"
75
+ ? portfolio.metrics.returnOnSpend
76
+ : portfolio.metrics.costPerSaleCents;
77
+ const averageText = average === null
78
+ ? null
79
+ : `${narrow ? "média" : "média da carteira"} ${format(average)}`;
80
+ const averageBox = average === null || averageText === null
81
+ ? null
82
+ : {
83
+ x: pad.left + 6,
84
+ y: sy(average) - 5 - 12,
85
+ width: averageText.length * 6.4,
86
+ height: 14,
87
+ };
88
+ // Each label takes the spot around its bubble that covers the least of the
89
+ // other bubbles, labels and the average line's caption, inside the chart.
90
+ const bubbles = points.map((point) => ({
91
+ point,
92
+ cx: sx(point.spendCents),
93
+ cy: sy(valueOf(point)),
94
+ r: radius(point.revenueCents),
95
+ }));
96
+ const bubbleBoxes = bubbles.map(({ cx, cy, r }) => ({
97
+ x: cx - r,
98
+ y: cy - r,
99
+ width: r * 2,
100
+ height: r * 2,
101
+ }));
102
+ // The band captions sit in the corners of their zones, as in the mockup.
103
+ const bandLegends = mode === "return"
104
+ ? [
105
+ {
106
+ band: "strong",
107
+ text: narrow
108
+ ? RETURN_BAND_LABELS.strong
109
+ : `acima de ${RETURN_BANDS.strong}x · ${RETURN_BAND_LABELS.strong}`,
110
+ y: sy(yMax) + 14,
111
+ },
112
+ {
113
+ band: "weak",
114
+ text: narrow
115
+ ? RETURN_BAND_LABELS.weak
116
+ : `abaixo de ${RETURN_BANDS.ok}x · ${RETURN_BAND_LABELS.weak}`,
117
+ y: sy(0) - 6,
118
+ },
119
+ ]
120
+ : [];
121
+ const placed = [
122
+ ...(averageBox ? [averageBox] : []),
123
+ ...bandLegends.map((legend) => ({
124
+ x: pad.left + 6,
125
+ y: legend.y - 11,
126
+ width: legend.text.length * 6,
127
+ height: 13,
128
+ })),
129
+ ];
130
+ const covered = (a, b) => overlaps(a, b)
131
+ ? (Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)) *
132
+ (Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y))
133
+ : 0;
134
+ const labels = bubbles.map(({ point, cx, cy, r }, index) => {
135
+ const value = format(valueOf(point));
136
+ const name = shortName(point.site.name, narrow ? 14 : 22);
137
+ const w = labelWidth(name, value);
138
+ const spot = (x, y, anchor) => ({
139
+ x,
140
+ y,
141
+ anchor,
142
+ width: w,
143
+ height: LABEL_HEIGHT,
144
+ });
145
+ const above = cy - r - 6 - LABEL_HEIGHT;
146
+ const below = cy + r + 4;
147
+ const middle = cy - LABEL_HEIGHT / 2;
148
+ const candidates = [
149
+ spot(cx - w / 2, above, "middle"),
150
+ spot(cx - w / 2, below, "middle"),
151
+ spot(cx + r + 6, middle, "start"),
152
+ spot(cx - r - 6 - w, middle, "end"),
153
+ spot(cx + r - w, above, "end"),
154
+ spot(cx + r - w, below, "end"),
155
+ spot(cx - r, above, "start"),
156
+ spot(cx - r, below, "start"),
157
+ ];
158
+ const obstacles = [
159
+ ...bubbleBoxes.filter((_, other) => other !== index),
160
+ ...placed,
161
+ ];
162
+ const outside = (box) => box.x < 2 ||
163
+ box.x + box.width > width - 2 ||
164
+ box.y < 0 ||
165
+ box.y + box.height > pad.top + plotHeight;
166
+ const score = (box) => (outside(box) ? 1e6 : 0) +
167
+ obstacles.reduce((sum, other) => sum + covered(box, other), 0);
168
+ const chosen = candidates.reduce((best, box) => score(box) < score(best) ? box : best);
169
+ placed.push(chosen);
170
+ const textX = chosen.anchor === "middle"
171
+ ? chosen.x + w / 2
172
+ : chosen.anchor === "start"
173
+ ? chosen.x
174
+ : chosen.x + w;
175
+ return {
176
+ key: point.site.siteId,
177
+ x: textX,
178
+ y: chosen.y + LABEL_HEIGHT - 4,
179
+ anchor: chosen.anchor,
180
+ name,
181
+ value,
182
+ };
183
+ });
184
+ const axisX = (cents) => cents === 0 ? "0" : compactMoney(cents, spendCurrency);
185
+ const axisY = (value) => mode === "return"
186
+ ? `${value.toLocaleString("pt-BR")}x`
187
+ : value === 0
188
+ ? "0"
189
+ : compactMoney(value, spendCurrency);
190
+ return (_jsx("div", { ref: ref, className: "rastro-portfolio__chart", children: _jsxs("svg", { width: width, height: height, viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": mode === "return"
191
+ ? "Gasto e retorno de cada cliente"
192
+ : "Gasto e custo por venda de cada cliente", children: [mode === "return" && (_jsxs(_Fragment, { children: [_jsx("rect", { className: "rastro-portfolio__zone", "data-band": "strong", x: pad.left, y: sy(yMax), width: plotWidth, height: sy(RETURN_BANDS.strong) - sy(yMax) }), _jsx("rect", { className: "rastro-portfolio__zone", "data-band": "weak", x: pad.left, y: sy(RETURN_BANDS.ok), width: plotWidth, height: sy(0) - sy(RETURN_BANDS.ok) })] })), yTicks.map((tick) => (_jsxs("g", { children: [_jsx("line", { className: "rastro-portfolio__grid", x1: pad.left, x2: width - pad.right, y1: sy(tick), y2: sy(tick) }), _jsx("text", { className: "rastro-portfolio__axis", x: pad.left - 6, y: sy(tick) + 4, textAnchor: "end", children: axisY(tick) })] }, `y${tick}`))), xTicks.map((tick) => (_jsxs("g", { children: [_jsx("line", { className: "rastro-portfolio__grid", x1: sx(tick), x2: sx(tick), y1: pad.top, y2: pad.top + plotHeight }), _jsx("text", { className: "rastro-portfolio__axis", x: sx(tick), y: height - 8, textAnchor: tick === 0 ? "start" : tick === xMax ? "end" : "middle", children: axisX(tick) })] }, `x${tick}`))), bandLegends.map((legend) => (_jsx("text", { className: "rastro-portfolio__zone-label", "data-band": legend.band, x: pad.left + 6, y: legend.y, children: legend.text }, legend.band))), average !== null && (_jsxs("g", { children: [_jsx("line", { className: "rastro-portfolio__average", x1: pad.left, x2: width - pad.right, y1: sy(average), y2: sy(average) }), _jsx("text", { className: "rastro-portfolio__average-label", x: pad.left + 6, y: sy(average) - 5, children: averageText })] })), bubbles.map(({ point, cx, cy, r }) => (_jsx("circle", { className: "rastro-portfolio__bubble", "data-band": mode === "return"
193
+ ? (returnBand(point.metrics.returnOnSpend) ?? undefined)
194
+ : undefined, cx: cx, cy: cy, r: r, children: _jsx("title", { children: `${point.site.name}: ${formatMoney(point.spendCents, spendCurrency)} gastos, ${format(valueOf(point))}` }) }, point.site.siteId))), labels.map((label) => (_jsxs("text", { className: "rastro-portfolio__label", x: label.x, y: label.y, textAnchor: label.anchor, children: [_jsx("tspan", { className: "rastro-portfolio__label-name", children: label.name }), _jsx("tspan", { dx: 5, className: "rastro-portfolio__label-value", children: label.value })] }, label.key)))] }) }));
195
+ }
196
+ //# sourceMappingURL=SpendChart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpendChart.js","sourceRoot":"","sources":["../../../src/ui/portfolio/SpendChart.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * What the agency view says in words: the "Precisa de atenção" alerts and the
3
+ * one-line insight on each client card. Every rule reads the figures, never a
4
+ * client's name, so the same sentences come out for any portfolio.
5
+ */
6
+ import type { LeadsDaily, LeadsReport, MetaStatus } from "../../client/index.js";
7
+ import { type ClientFigures, type PortfolioFigures, type PortfolioSite } from "./figures.js";
8
+ /** "bad" asks for action, "watch" for a look, "good" is an opportunity. */
9
+ export type PortfolioTone = "bad" | "watch" | "good";
10
+ export interface PortfolioAlert {
11
+ /** Stable key: the rule and the client. */
12
+ id: string;
13
+ kind: "metaSync" | "costPerSaleUp" | "fewSalesHighTicket" | "topAd";
14
+ tone: PortfolioTone;
15
+ siteId: string;
16
+ siteName: string;
17
+ /** What happened, after the client's name: "custo por venda subiu 18%…". */
18
+ headline: string;
19
+ /** Why it matters, in a sentence or two. */
20
+ detail: string;
21
+ /** The link's label. */
22
+ action: string;
23
+ /** The ad the alert is about, for the drawer of Campanhas. */
24
+ adId?: string;
25
+ }
26
+ export interface PortfolioInsight {
27
+ /** Which rule said it; "bestReturn" marks the portfolio's best card. */
28
+ kind: "syncing" | "noSpend" | "noSales" | "bestReturn" | "fewSalesHighTicket" | "cheapLeads" | "weakReturn" | "leadingCampaign" | "band" | "sales";
29
+ tone: PortfolioTone | "neutral";
30
+ text: string;
31
+ }
32
+ /** The thresholds the rules use, exported so hosts and tests can read them. */
33
+ export declare const PORTFOLIO_RULES: Readonly<{
34
+ /** Days in each window of the week-over-week comparison. */
35
+ weekDays: 7;
36
+ /**
37
+ * Days left out at the end of the series: a lead is counted on the day it
38
+ * arrived, so the newest week has not finished selling yet.
39
+ */
40
+ maturingDays: 7;
41
+ /** Sales each week needs before its cost per sale means anything. */
42
+ minWeeklySales: 3;
43
+ /** A cost per sale this much higher than the week before raises an alert. */
44
+ costPerSaleRise: 0.15;
45
+ /** "Few sales": at most this many in the period… */
46
+ fewSales: 5;
47
+ /** …with an average sale this many times the portfolio's. */
48
+ highTicketFactor: 3;
49
+ /** A top ad needs this many ads beside it to stand out… */
50
+ minAdsForTopAd: 3;
51
+ /** …and this many times its fair share (1 ÷ ads) of the client's sales. */
52
+ topAdShareFactor: 2;
53
+ /** A campaign that makes at least this share of the sales "pulls" them. */
54
+ leadingCampaignShare: 0.5;
55
+ }>;
56
+ /**
57
+ * The "Precisa de atenção" list: a broken Meta sync, then problems, what to
58
+ * watch and what to grow; within a tone, in the order of `sites`. Pass
59
+ * `limit` to cut it; the view shows the first few and says how many are left.
60
+ */
61
+ export declare function portfolioAlerts({ sites, report, dailyBySite, status, limit, }: {
62
+ sites: readonly PortfolioSite[];
63
+ report: LeadsReport;
64
+ dailyBySite?: Record<string, LeadsDaily> | undefined;
65
+ status?: MetaStatus | undefined;
66
+ limit?: number;
67
+ }): PortfolioAlert[];
68
+ /**
69
+ * One line for a client's card, the first rule that holds: no Meta, no sales,
70
+ * the portfolio's best return, few sales with a big ticket, cheap leads that
71
+ * buy little, a weak return, one campaign carrying the sales, or the band.
72
+ */
73
+ export declare function clientInsight(client: ClientFigures, portfolio: PortfolioFigures): PortfolioInsight;
74
+ //# sourceMappingURL=alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../../src/ui/portfolio/alerts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACX,MAAM,uBAAuB,CAAC;AAS/B,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAEnB,MAAM,cAAc,CAAC;AAGtB,2EAA2E;AAC3E,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,2CAA2C;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,eAAe,GAAG,oBAAoB,GAAG,OAAO,CAAC;IACpE,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,IAAI,EACA,SAAS,GACT,SAAS,GACT,SAAS,GACT,YAAY,GACZ,oBAAoB,GACpB,YAAY,GACZ,YAAY,GACZ,iBAAiB,GACjB,MAAM,GACN,OAAO,CAAC;IACZ,IAAI,EAAE,aAAa,GAAG,SAAS,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,+EAA+E;AAC/E,eAAO,MAAM,eAAe;IAC1B,4DAA4D;;IAE5D;;;OAGG;;IAEH,qEAAqE;;IAErE,6EAA6E;;IAE7E,oDAAoD;;IAEpD,6DAA6D;;IAE7D,2DAA2D;;IAE3D,2EAA2E;;IAE3E,2EAA2E;;EAE3E,CAAC;AAkNH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,MAAM,EACN,WAAW,EACX,MAAM,EACN,KAAgB,GACjB,EAAE;IACD,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC;IACrD,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,cAAc,EAAE,CAkBnB;AAKD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,gBAAgB,GAC1B,gBAAgB,CA8ElB"}
@@ -0,0 +1,291 @@
1
+ import { change, formatMoney, formatReturn, RETURN_BANDS, returnBand, rowMetrics, } from "../metrics.js";
2
+ import { portfolioFigures, spendState, } from "./figures.js";
3
+ import { compactMoney } from "./format.js";
4
+ /** The thresholds the rules use, exported so hosts and tests can read them. */
5
+ export const PORTFOLIO_RULES = Object.freeze({
6
+ /** Days in each window of the week-over-week comparison. */
7
+ weekDays: 7,
8
+ /**
9
+ * Days left out at the end of the series: a lead is counted on the day it
10
+ * arrived, so the newest week has not finished selling yet.
11
+ */
12
+ maturingDays: 7,
13
+ /** Sales each week needs before its cost per sale means anything. */
14
+ minWeeklySales: 3,
15
+ /** A cost per sale this much higher than the week before raises an alert. */
16
+ costPerSaleRise: 0.15,
17
+ /** "Few sales": at most this many in the period… */
18
+ fewSales: 5,
19
+ /** …with an average sale this many times the portfolio's. */
20
+ highTicketFactor: 3,
21
+ /** A top ad needs this many ads beside it to stand out… */
22
+ minAdsForTopAd: 3,
23
+ /** …and this many times its fair share (1 ÷ ads) of the client's sales. */
24
+ topAdShareFactor: 2,
25
+ /** A campaign that makes at least this share of the sales "pulls" them. */
26
+ leadingCampaignShare: 0.5,
27
+ });
28
+ const TONE_ORDER = {
29
+ bad: 0,
30
+ watch: 1,
31
+ good: 2,
32
+ };
33
+ /** A broken Meta sync outranks everything: every other number goes stale. */
34
+ const rank = (alert) => alert.kind === "metaSync" ? -1 : TONE_ORDER[alert.tone];
35
+ const sales = (count) => (count === 1 ? "1 venda" : `${count} vendas`);
36
+ /**
37
+ * The last week that finished selling against the week before it: with a
38
+ * series of N days, days N−14…N−8 against N−21…N−15.
39
+ */
40
+ function matureWeeks(daily) {
41
+ const { weekDays: days, maturingDays: skip } = PORTFOLIO_RULES;
42
+ const sorted = [...daily].sort((a, b) => a.dayStart - b.dayStart);
43
+ const end = sorted.length - skip;
44
+ if (end - days * 2 < 0)
45
+ return null;
46
+ const sum = (rows) => rows.reduce((acc, row) => ({
47
+ won: acc.won + row.won,
48
+ spendCents: acc.spendCents === null || row.spendCents === null
49
+ ? null
50
+ : acc.spendCents + row.spendCents,
51
+ }), { won: 0, spendCents: 0 });
52
+ return {
53
+ current: sum(sorted.slice(end - days, end)),
54
+ previous: sum(sorted.slice(end - days * 2, end - days)),
55
+ /** The UTC day the compared week starts, for "na semana de 14/09". */
56
+ weekStart: sorted[end - days].dayStart,
57
+ };
58
+ }
59
+ /** "14/09": a UTC day as the screens write it. */
60
+ const dayMonth = (dayStart) => new Date(dayStart).toLocaleDateString("pt-BR", {
61
+ day: "2-digit",
62
+ month: "2-digit",
63
+ timeZone: "UTC",
64
+ });
65
+ const costPerSale = (week) => rowMetrics({ ...week, leads: 0, qualified: 0, revenueCents: 0 })
66
+ .costPerSaleCents;
67
+ function costPerSaleAlert(client, daily, portfolio) {
68
+ if (!daily || client.spendCents === null)
69
+ return null;
70
+ const weeks = matureWeeks(daily);
71
+ if (!weeks)
72
+ return null;
73
+ const { current, previous, weekStart } = weeks;
74
+ if (current.won < PORTFOLIO_RULES.minWeeklySales ||
75
+ previous.won < PORTFOLIO_RULES.minWeeklySales) {
76
+ return null;
77
+ }
78
+ const now = costPerSale(current);
79
+ const before = costPerSale(previous);
80
+ const rise = change(now, before);
81
+ if (rise.relative === null || rise.relative < PORTFOLIO_RULES.costPerSaleRise)
82
+ return null;
83
+ const money = (cents) => formatMoney(cents, client.spendCurrency);
84
+ const cheapLeads = client.spendCurrency === portfolio.spendCurrency &&
85
+ client.metrics.costPerLeadCents !== null &&
86
+ portfolio.metrics.costPerLeadCents !== null &&
87
+ client.metrics.costPerLeadCents <= portfolio.metrics.costPerLeadCents;
88
+ return {
89
+ id: `costPerSaleUp:${client.site.siteId}`,
90
+ kind: "costPerSaleUp",
91
+ tone: "bad",
92
+ siteId: client.site.siteId,
93
+ siteName: client.site.name,
94
+ headline: `custo por venda subiu ${rise.label?.replace("+", "")} na semana de ${dayMonth(weekStart)}`,
95
+ detail: `Foi de ${money(before)} para ${money(now)}. ${cheapLeads
96
+ ? `Os leads seguem baratos (${money(client.metrics.costPerLeadCents)}), mas estão comprando menos.`
97
+ : "Vale ver quais anúncios gastaram mais sem vender."}`,
98
+ action: `Ver campanhas da ${client.site.name}`,
99
+ };
100
+ }
101
+ /** Few sales with a big ticket: one sale more or less swings the month. */
102
+ function isFewSalesHighTicket(client, portfolio) {
103
+ return (client.won > 0 &&
104
+ client.won <= PORTFOLIO_RULES.fewSales &&
105
+ client.ticketCents !== null &&
106
+ portfolio.ticketCents !== null &&
107
+ client.ticketCents >=
108
+ portfolio.ticketCents * PORTFOLIO_RULES.highTicketFactor);
109
+ }
110
+ function fewSalesAlert(client, portfolio) {
111
+ if (!isFewSalesHighTicket(client, portfolio))
112
+ return null;
113
+ const qualified = client.qualified === 1
114
+ ? "o 1 qualificado"
115
+ : `os ${client.qualified} qualificados`;
116
+ return {
117
+ id: `fewSalesHighTicket:${client.site.siteId}`,
118
+ kind: "fewSalesHighTicket",
119
+ tone: "watch",
120
+ siteId: client.site.siteId,
121
+ siteName: client.site.name,
122
+ headline: `${sales(client.won)}, mas ticket médio de ${compactMoney(client.ticketCents, portfolio.currency)}`,
123
+ detail: `Uma venda a mais ou a menos muda o mês inteiro. Vale olhar ${qualified}.`,
124
+ action: `Abrir campanhas da ${client.site.name}`,
125
+ };
126
+ }
127
+ /**
128
+ * The ad that sells far more than its share, among several, strictly ahead of
129
+ * the runner-up, at a strong return.
130
+ */
131
+ function topAd(client) {
132
+ const [best, second] = client.ads;
133
+ if (!best ||
134
+ (second && second.won >= best.won) ||
135
+ client.ads.length < PORTFOLIO_RULES.minAdsForTopAd ||
136
+ client.won === 0 ||
137
+ best.won / client.won < PORTFOLIO_RULES.topAdShareFactor / client.ads.length) {
138
+ return null;
139
+ }
140
+ const returnOnSpend = metricsInRevenueCurrency(client, {
141
+ leads: 0,
142
+ qualified: 0,
143
+ won: best.won,
144
+ revenueCents: best.revenueCents,
145
+ spendCents: best.spendCents,
146
+ });
147
+ if (returnBand(returnOnSpend) !== "strong")
148
+ return null;
149
+ return { ad: best, returnOnSpend };
150
+ }
151
+ /** An ad's return, or null when its account bills in another currency. */
152
+ function metricsInRevenueCurrency(client, row) {
153
+ return client.metrics.returnOnSpend === null && client.spendCents !== null
154
+ ? null
155
+ : rowMetrics(row).returnOnSpend;
156
+ }
157
+ function topAdAlert(client) {
158
+ const top = topAd(client);
159
+ if (!top)
160
+ return null;
161
+ return {
162
+ id: `topAd:${client.site.siteId}`,
163
+ kind: "topAd",
164
+ tone: "good",
165
+ siteId: client.site.siteId,
166
+ siteName: client.site.name,
167
+ headline: `criativo “${top.ad.name}” é o que mais vende`,
168
+ detail: `${top.ad.won} das ${client.won} vendas, com retorno de ${formatReturn(top.returnOnSpend)}. Dá pra pôr mais verba nele.`,
169
+ action: "Abrir o anúncio",
170
+ adId: top.ad.adId,
171
+ };
172
+ }
173
+ /** An ad account Meta stopped syncing: the client's numbers go stale. */
174
+ function metaSyncAlert(client) {
175
+ const broken = client.accounts.find((account) => account.syncState === "error" || account.lastError);
176
+ if (!broken)
177
+ return null;
178
+ return {
179
+ id: `metaSync:${client.site.siteId}`,
180
+ kind: "metaSync",
181
+ tone: "bad",
182
+ siteId: client.site.siteId,
183
+ siteName: client.site.name,
184
+ headline: `a Meta parou de sincronizar a conta “${broken.name}”`,
185
+ detail: `${broken.lastError ? `A Meta respondeu: ${broken.lastError}. ` : ""}O gasto deste cliente pode estar desatualizado até a conta ser conectada de novo.`,
186
+ action: `Ver campanhas da ${client.site.name}`,
187
+ };
188
+ }
189
+ /**
190
+ * The "Precisa de atenção" list: a broken Meta sync, then problems, what to
191
+ * watch and what to grow; within a tone, in the order of `sites`. Pass
192
+ * `limit` to cut it; the view shows the first few and says how many are left.
193
+ */
194
+ export function portfolioAlerts({ sites, report, dailyBySite, status, limit = Infinity, }) {
195
+ const portfolio = portfolioFigures({ sites, report, status });
196
+ const alerts = [];
197
+ for (const client of portfolio.clients) {
198
+ for (const alert of [
199
+ metaSyncAlert(client),
200
+ costPerSaleAlert(client, dailyBySite?.[client.site.siteId], portfolio),
201
+ fewSalesAlert(client, portfolio),
202
+ topAdAlert(client),
203
+ ]) {
204
+ if (alert)
205
+ alerts.push(alert);
206
+ }
207
+ }
208
+ return alerts
209
+ .map((alert, index) => ({ alert, index }))
210
+ .sort((a, b) => rank(a.alert) - rank(b.alert) || a.index - b.index)
211
+ .slice(0, limit)
212
+ .map(({ alert }) => alert);
213
+ }
214
+ /** The label a campaign goes by: "Coleção Verão · Conversas" → "Coleção Verão". */
215
+ const campaignLabel = (name) => name.split(" · ")[0]?.trim() || name;
216
+ /**
217
+ * One line for a client's card, the first rule that holds: no Meta, no sales,
218
+ * the portfolio's best return, few sales with a big ticket, cheap leads that
219
+ * buy little, a weak return, one campaign carrying the sales, or the band.
220
+ */
221
+ export function clientInsight(client, portfolio) {
222
+ if (client.spendCents === null) {
223
+ // A linked account without spend yet is syncing, not missing.
224
+ return spendState(client, portfolio) === "syncing"
225
+ ? { kind: "syncing", tone: "neutral", text: "Sincronizando com a Meta" }
226
+ : { kind: "noSpend", tone: "neutral", text: "Meta não conectada" };
227
+ }
228
+ if (client.won === 0) {
229
+ return {
230
+ kind: "noSales",
231
+ tone: "watch",
232
+ text: "Ainda sem vendas de anúncio",
233
+ };
234
+ }
235
+ const returnOnSpend = client.metrics.returnOnSpend;
236
+ const withReturn = portfolio.clients.filter((other) => other.metrics.returnOnSpend !== null);
237
+ if (returnOnSpend !== null &&
238
+ withReturn.length >= 2 &&
239
+ withReturn.every((other) => other === client || other.metrics.returnOnSpend < returnOnSpend)) {
240
+ return {
241
+ kind: "bestReturn",
242
+ tone: "good",
243
+ text: "Melhor retorno da carteira",
244
+ };
245
+ }
246
+ if (isFewSalesHighTicket(client, portfolio)) {
247
+ return {
248
+ kind: "fewSalesHighTicket",
249
+ tone: "watch",
250
+ text: "Poucas vendas, ticket alto",
251
+ };
252
+ }
253
+ const band = returnBand(returnOnSpend);
254
+ if (band === "weak") {
255
+ const cheap = client.spendCurrency === portfolio.spendCurrency &&
256
+ client.metrics.costPerLeadCents !== null &&
257
+ portfolio.metrics.costPerLeadCents !== null &&
258
+ client.metrics.costPerLeadCents < portfolio.metrics.costPerLeadCents &&
259
+ client.ticketCents !== null &&
260
+ portfolio.ticketCents !== null &&
261
+ client.ticketCents < portfolio.ticketCents;
262
+ return cheap
263
+ ? { kind: "cheapLeads", tone: "bad", text: "Lead barato, ticket baixo" }
264
+ : {
265
+ kind: "weakReturn",
266
+ tone: "bad",
267
+ text: `Retorno abaixo de ${RETURN_BANDS.ok}x`,
268
+ };
269
+ }
270
+ const [leading] = client.campaigns;
271
+ if (leading &&
272
+ client.campaigns.length >= 2 &&
273
+ leading.won / client.won >= PORTFOLIO_RULES.leadingCampaignShare) {
274
+ return {
275
+ kind: "leadingCampaign",
276
+ tone: "good",
277
+ text: `${campaignLabel(leading.name)} puxando as vendas`,
278
+ };
279
+ }
280
+ if (band === "strong") {
281
+ return { kind: "band", tone: "good", text: "Retorno forte" };
282
+ }
283
+ if (band === "ok")
284
+ return { kind: "band", tone: "watch", text: "Retorno ok" };
285
+ return {
286
+ kind: "sales",
287
+ tone: "neutral",
288
+ text: `${sales(client.won)} no período`,
289
+ };
290
+ }
291
+ //# sourceMappingURL=alerts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../../src/ui/portfolio/alerts.ts"],"names":[],"mappings":"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"}