@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,106 @@
1
+ /**
2
+ * The derived figures every Rastro screen shows, computed one way only. Reports
3
+ * carry totals (spend, leads, qualified, sales, revenue) and never a stored
4
+ * ratio, so a campaign row, a creative card and an agency card always agree.
5
+ * Pure functions: no React, no Convex, safe in any host.
6
+ */
7
+ /** The counters a report row carries; spend is null without Meta data. */
8
+ export interface FunnelRow {
9
+ leads: number;
10
+ qualified: number;
11
+ won: number;
12
+ revenueCents: number;
13
+ spendCents?: number | null;
14
+ }
15
+ export interface RowMetrics {
16
+ /** Spend per lead, in cents; null without spend or without leads. */
17
+ costPerLeadCents: number | null;
18
+ /** Spend per qualified lead, in cents. */
19
+ costPerQualifiedCents: number | null;
20
+ /** Spend per sale, in cents. */
21
+ costPerSaleCents: number | null;
22
+ /** Revenue per unit of spend ("9,3x"); null without spend. */
23
+ returnOnSpend: number | null;
24
+ }
25
+ export declare function rowMetrics(row: FunnelRow): RowMetrics;
26
+ /** The screen's labels: "forte", "ok", "fraco". */
27
+ export type ReturnBand = "strong" | "ok" | "weak";
28
+ /** Where the bands start: ≥ 8x is strong, ≥ 4x is ok, below is weak. */
29
+ export declare const RETURN_BANDS: Readonly<{
30
+ strong: 8;
31
+ ok: 4;
32
+ }>;
33
+ export declare function returnBand(returnOnSpend: number | null, thresholds?: {
34
+ strong: number;
35
+ ok: number;
36
+ }): ReturnBand | null;
37
+ export declare const RETURN_BAND_LABELS: Record<ReturnBand, string>;
38
+ /**
39
+ * Money from integer cents: "R$ 4.200" when there are no cents, "R$ 13,46"
40
+ * when there are. An em dash for unknown values, never "R$ 0".
41
+ */
42
+ export declare function formatMoney(cents: number | null | undefined, currency?: string, locale?: string): string;
43
+ /** "9,3x" (one decimal), or an em dash without a return. */
44
+ export declare function formatReturn(returnOnSpend: number | null, locale?: string): string;
45
+ /** "37,8%" of `whole`, or an em dash when there is nothing to divide. */
46
+ export declare function formatShare(part: number, whole: number, locale?: string): string;
47
+ export interface Change {
48
+ /** Relative change (0.08 = +8%); null when the previous value is zero. */
49
+ relative: number | null;
50
+ /** "+8%", "−9%", "0%", or null. */
51
+ label: string | null;
52
+ }
53
+ /** Change against the previous period, rounded to whole percent. */
54
+ export declare function change(current: number | null | undefined, previous: number | null | undefined): Change;
55
+ /** Returns compare in absolute points: "+1,6x". */
56
+ export declare function returnChange(current: number | null, previous: number | null, locale?: string): string | null;
57
+ /** The ad-bought counters of one site, as `leads.report` returns them. */
58
+ export interface SitePaidRow {
59
+ siteId: string;
60
+ paid: FunnelRow;
61
+ spendCents?: number | null;
62
+ /** The ad account's currency for this site's spend. */
63
+ spendCurrency?: string | null;
64
+ /** Spend in a currency other than the site's own revenue. */
65
+ currencyMismatch?: boolean;
66
+ }
67
+ /** The report fields `paidFunnel` reads, typed by shape. */
68
+ export interface PaidFunnelReport {
69
+ totals: {
70
+ spendCents?: number | null;
71
+ };
72
+ origins: ReadonlyArray<FunnelRow & {
73
+ key: string;
74
+ }>;
75
+ bySite?: ReadonlyArray<SitePaidRow> | null;
76
+ }
77
+ export interface PaidFunnel {
78
+ /** What spend bought: only the sites whose spend is known are summed. */
79
+ row: FunnelRow & {
80
+ spendCents: number | null;
81
+ };
82
+ /** True when a site with ad leads has no known spend (left out of `row`). */
83
+ partial: boolean;
84
+ /** Those sites, in the report's order. */
85
+ sitesWithoutSpend: string[];
86
+ /** The currency of `row.spendCents`, when the sites report one. */
87
+ spendCurrency: string | null;
88
+ /**
89
+ * True when `row` sums spend in another currency than the revenue (no site
90
+ * bills in its own): spend and costs show, the return does not.
91
+ */
92
+ currencyMismatch: boolean;
93
+ }
94
+ /**
95
+ * The paid funnel of a report: the ad-bought leads next to the spend that
96
+ * bought them. Cost per lead and return must never divide one site's spend by
97
+ * every site's leads, so a site without Meta data stays out of `row` (and the
98
+ * screen says so through `partial`). With no site's spend known, `row` is
99
+ * every site's paid leads with unknown spend. The sites billing in their own
100
+ * currency are summed; when none does, every site with known spend is, in the
101
+ * account currency and flagged `currencyMismatch`, so spend never vanishes.
102
+ * Spend in two currencies cannot be summed, so `row.spendCents` is null. A host
103
+ * without `bySite` falls back to the `ctwa_ad` origin and the total spend.
104
+ */
105
+ export declare function paidFunnel(report: PaidFunnelReport): PaidFunnel;
106
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/ui/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * The derived figures every Rastro screen shows, computed one way only. Reports
3
+ * carry totals (spend, leads, qualified, sales, revenue) and never a stored
4
+ * ratio, so a campaign row, a creative card and an agency card always agree.
5
+ * Pure functions: no React, no Convex, safe in any host.
6
+ */
7
+ const perUnit = (spend, count) => count > 0 ? Math.round(spend / count) : null;
8
+ export function rowMetrics(row) {
9
+ const spend = row.spendCents;
10
+ if (spend === null || spend === undefined) {
11
+ return {
12
+ costPerLeadCents: null,
13
+ costPerQualifiedCents: null,
14
+ costPerSaleCents: null,
15
+ returnOnSpend: null,
16
+ };
17
+ }
18
+ return {
19
+ costPerLeadCents: perUnit(spend, row.leads),
20
+ costPerQualifiedCents: perUnit(spend, row.qualified),
21
+ costPerSaleCents: perUnit(spend, row.won),
22
+ returnOnSpend: spend > 0 ? row.revenueCents / spend : null,
23
+ };
24
+ }
25
+ /** Where the bands start: ≥ 8x is strong, ≥ 4x is ok, below is weak. */
26
+ export const RETURN_BANDS = Object.freeze({ strong: 8, ok: 4 });
27
+ export function returnBand(returnOnSpend, thresholds = RETURN_BANDS) {
28
+ if (returnOnSpend === null || !Number.isFinite(returnOnSpend))
29
+ return null;
30
+ if (returnOnSpend >= thresholds.strong)
31
+ return "strong";
32
+ if (returnOnSpend >= thresholds.ok)
33
+ return "ok";
34
+ return "weak";
35
+ }
36
+ export const RETURN_BAND_LABELS = {
37
+ strong: "forte",
38
+ ok: "ok",
39
+ weak: "fraco",
40
+ };
41
+ const moneyFormats = new Map();
42
+ /**
43
+ * Money from integer cents: "R$ 4.200" when there are no cents, "R$ 13,46"
44
+ * when there are. An em dash for unknown values, never "R$ 0".
45
+ */
46
+ export function formatMoney(cents, currency = "BRL", locale = "pt-BR") {
47
+ if (cents === null || cents === undefined || !Number.isFinite(cents)) {
48
+ return "—";
49
+ }
50
+ const whole = cents % 100 === 0;
51
+ const key = `${locale}:${currency}:${whole}`;
52
+ let format = moneyFormats.get(key);
53
+ if (!format) {
54
+ format = new Intl.NumberFormat(locale, {
55
+ style: "currency",
56
+ currency,
57
+ minimumFractionDigits: whole ? 0 : 2,
58
+ maximumFractionDigits: whole ? 0 : 2,
59
+ });
60
+ moneyFormats.set(key, format);
61
+ }
62
+ return format.format(cents / 100).replace(/[\u00a0\u202f]/g, " ");
63
+ }
64
+ const oneDecimal = (value, locale) => value.toLocaleString(locale, {
65
+ minimumFractionDigits: 1,
66
+ maximumFractionDigits: 1,
67
+ });
68
+ /** "9,3x" (one decimal), or an em dash without a return. */
69
+ export function formatReturn(returnOnSpend, locale = "pt-BR") {
70
+ if (returnOnSpend === null || !Number.isFinite(returnOnSpend))
71
+ return "—";
72
+ return `${oneDecimal(returnOnSpend, locale)}x`;
73
+ }
74
+ /** "37,8%" of `whole`, or an em dash when there is nothing to divide. */
75
+ export function formatShare(part, whole, locale = "pt-BR") {
76
+ if (!(whole > 0))
77
+ return "—";
78
+ return `${oneDecimal((part / whole) * 100, locale)}%`;
79
+ }
80
+ /** Change against the previous period, rounded to whole percent. */
81
+ export function change(current, previous) {
82
+ if (current === null ||
83
+ current === undefined ||
84
+ previous === null ||
85
+ previous === undefined ||
86
+ previous === 0) {
87
+ return { relative: null, label: null };
88
+ }
89
+ const relative = (current - previous) / Math.abs(previous);
90
+ const percent = Math.round(relative * 100);
91
+ const sign = percent > 0 ? "+" : percent < 0 ? "−" : "";
92
+ return { relative, label: `${sign}${Math.abs(percent)}%` };
93
+ }
94
+ /** Returns compare in absolute points: "+1,6x". */
95
+ export function returnChange(current, previous, locale = "pt-BR") {
96
+ if (current === null || previous === null)
97
+ return null;
98
+ const delta = Math.round((current - previous) * 10) / 10;
99
+ const sign = delta > 0 ? "+" : delta < 0 ? "−" : "";
100
+ return `${sign}${oneDecimal(Math.abs(delta), locale)}x`;
101
+ }
102
+ const AD_ORIGIN_KEY = "ctwa_ad";
103
+ /**
104
+ * The paid funnel of a report: the ad-bought leads next to the spend that
105
+ * bought them. Cost per lead and return must never divide one site's spend by
106
+ * every site's leads, so a site without Meta data stays out of `row` (and the
107
+ * screen says so through `partial`). With no site's spend known, `row` is
108
+ * every site's paid leads with unknown spend. The sites billing in their own
109
+ * currency are summed; when none does, every site with known spend is, in the
110
+ * account currency and flagged `currencyMismatch`, so spend never vanishes.
111
+ * Spend in two currencies cannot be summed, so `row.spendCents` is null. A host
112
+ * without `bySite` falls back to the `ctwa_ad` origin and the total spend.
113
+ */
114
+ export function paidFunnel(report) {
115
+ const sites = report.bySite ?? [];
116
+ if (sites.length === 0) {
117
+ const origin = report.origins.find((row) => row.key === AD_ORIGIN_KEY);
118
+ return {
119
+ row: {
120
+ leads: origin?.leads ?? 0,
121
+ qualified: origin?.qualified ?? 0,
122
+ won: origin?.won ?? 0,
123
+ revenueCents: origin?.revenueCents ?? 0,
124
+ spendCents: report.totals.spendCents ?? null,
125
+ },
126
+ partial: false,
127
+ sitesWithoutSpend: [],
128
+ spendCurrency: null,
129
+ currencyMismatch: false,
130
+ };
131
+ }
132
+ const spent = sites.filter((site) => site.spendCents != null);
133
+ const comparable = spent.filter((site) => !site.currencyMismatch);
134
+ // The comparable sites, or, when none is, every site with known spend.
135
+ const pool = comparable.length > 0 ? comparable : spent;
136
+ const currencies = new Set(pool
137
+ .map((site) => site.spendCurrency)
138
+ .filter((currency) => Boolean(currency)));
139
+ // Two currencies cannot be summed: every site's leads, spend unknown.
140
+ const known = currencies.size > 1 ? [] : pool;
141
+ const unknown = sites.filter((site) => !known.includes(site) && site.paid.leads > 0);
142
+ const summed = known.length > 0 ? known : sites;
143
+ const row = {
144
+ leads: 0,
145
+ qualified: 0,
146
+ won: 0,
147
+ revenueCents: 0,
148
+ spendCents: known.length > 0 ? 0 : null,
149
+ };
150
+ for (const site of summed) {
151
+ row.leads += site.paid.leads;
152
+ row.qualified += site.paid.qualified;
153
+ row.won += site.paid.won;
154
+ row.revenueCents += site.paid.revenueCents;
155
+ if (row.spendCents !== null)
156
+ row.spendCents += site.spendCents ?? 0;
157
+ }
158
+ return {
159
+ row,
160
+ partial: known.length > 0 && unknown.length > 0,
161
+ sitesWithoutSpend: unknown.map((site) => site.siteId),
162
+ spendCurrency: known.length > 0 ? ([...currencies][0] ?? null) : null,
163
+ currencyMismatch: known.length > 0 && comparable.length === 0,
164
+ };
165
+ }
166
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/ui/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { LeadsDaily, LeadsReport, MetaStatus } from "../../client/index.js";
2
+ import { type PortfolioSite } from "./figures.js";
3
+ export interface PortfolioViewProps {
4
+ /** The agency's clients, in the order the cards show them. */
5
+ sites: readonly PortfolioSite[];
6
+ /** `leads.report` over every client (with `bySite`); undefined while loading. */
7
+ report: LeadsReport | undefined;
8
+ /** The same report for the period before, for the KPI changes. */
9
+ previous?: LeadsReport | undefined;
10
+ /** `leads.daily` per client with `origin: "ctwa_ad"`, for sparklines and alerts. */
11
+ dailyBySite?: Record<string, LeadsDaily> | undefined;
12
+ /** `meta.status` for the clients: ad accounts and their last sync. */
13
+ status?: MetaStatus | undefined;
14
+ /** Opens a client's Campanhas, optionally on one ad. */
15
+ onOpenSite: (siteId: string, options?: {
16
+ adId?: string;
17
+ }) => void;
18
+ now: number;
19
+ loading?: boolean;
20
+ }
21
+ /**
22
+ * The agency's home: every client from spend to sale. Totals of the leads the
23
+ * ads bought, a card per client, spend against return, and what needs a look.
24
+ */
25
+ export declare function PortfolioView({ sites, report, previous, dailyBySite, status, onOpenSite, now, loading, }: PortfolioViewProps): import("react").JSX.Element;
26
+ //# sourceMappingURL=PortfolioView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortfolioView.d.ts","sourceRoot":"","sources":["../../../src/ui/portfolio/PortfolioView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACX,MAAM,uBAAuB,CAAC;AAa/B,OAAO,EAIL,KAAK,aAAa,EAGnB,MAAM,cAAc,CAAC;AAItB,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC,iFAAiF;IACjF,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACnC,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC;IACrD,sEAAsE;IACtE,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,wDAAwD;IACxD,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAslBD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,MAAM,EACN,QAAQ,EACR,WAAW,EACX,MAAM,EACN,UAAU,EACV,GAAG,EACH,OAAO,GACR,EAAE,kBAAkB,+BAwGpB"}
@@ -0,0 +1,216 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { change, formatMoney, formatReturn, returnBand, returnChange, } from "../metrics.js";
4
+ import { clientInsight, portfolioAlerts, } from "./alerts.js";
5
+ import { portfolioFigures, spendState, } from "./figures.js";
6
+ import { hueSlot, initials } from "./format.js";
7
+ import { SpendChart } from "./SpendChart.js";
8
+ const MINUTE_MS = 60_000;
9
+ /** The stalest linked account's last sync: the portfolio is as fresh as it. */
10
+ function syncedAgo(status, now) {
11
+ const syncs = (status ?? []).flatMap((site) => site.adAccounts.map((account) => account.lastSyncAt ?? 0));
12
+ if (syncs.length === 0)
13
+ return null;
14
+ const oldest = Math.min(...syncs);
15
+ if (!oldest)
16
+ return "sincronizando";
17
+ const minutes = Math.max(0, Math.round((now - oldest) / MINUTE_MS));
18
+ if (minutes < 1)
19
+ return "agora";
20
+ if (minutes < 60)
21
+ return `há ${minutes} min`;
22
+ const hours = Math.round(minutes / 60);
23
+ return hours < 24 ? `há ${hours} h` : `há ${Math.round(hours / 24)} d`;
24
+ }
25
+ function Kpi({ label, value, sub, delta, highlight, }) {
26
+ return (_jsxs("div", { className: "rastro-card rastro-portfolio__kpi", "data-highlight": highlight ? "" : undefined, children: [_jsx("span", { className: "rastro-portfolio__kpi-label", children: label }), _jsx("strong", { className: "rastro-num rastro-portfolio__kpi-value", children: value }), _jsxs("span", { className: "rastro-portfolio__kpi-sub", children: [delta?.label && (_jsx("span", { className: "rastro-change", "data-trend": delta.trend, children: delta.label })), sub] })] }));
27
+ }
28
+ const trendOf = (relative, higherIsGood = true) => relative === null || relative === 0
29
+ ? undefined
30
+ : relative > 0 === higherIsGood
31
+ ? "good"
32
+ : "bad";
33
+ function Kpis({ portfolio, previous, accounts, synced, }) {
34
+ const { paid, metrics, spendCurrency, currency } = portfolio;
35
+ const { row } = paid;
36
+ // No spend yet, but a linked account: the first sync is still running.
37
+ const noSpend = row.spendCents === null &&
38
+ portfolio.clients.some((client) => spendState(client, portfolio) === "syncing")
39
+ ? "sincronizando com a Meta"
40
+ : "Meta não conectada";
41
+ const before = previous?.paid.row;
42
+ const delta = (current, old) => {
43
+ const result = change(current, old);
44
+ return { label: result.label, trend: trendOf(result.relative) };
45
+ };
46
+ const returnDelta = returnChange(metrics.returnOnSpend, previous?.metrics.returnOnSpend ?? null);
47
+ const spendSub = spendCurrency === null
48
+ ? "contas em moedas diferentes"
49
+ : row.spendCents === null
50
+ ? noSpend
51
+ : [
52
+ accounts === 0
53
+ ? null
54
+ : accounts === 1
55
+ ? "1 conta de anúncio"
56
+ : `${accounts} contas de anúncio`,
57
+ synced,
58
+ ]
59
+ .filter(Boolean)
60
+ .join(" · ");
61
+ const per = (cents, unit) => cents === null || spendCurrency === null
62
+ ? null
63
+ : `${formatMoney(cents, spendCurrency)} por ${unit}`;
64
+ const returnSub = metrics.returnOnSpend !== null
65
+ ? `cada ${formatMoney(100, currency)} na Meta voltou ${formatMoney(Math.round(metrics.returnOnSpend * 100), currency)}`
66
+ : spendCurrency === null
67
+ ? "contas em moedas diferentes"
68
+ : portfolio.currencyMismatch
69
+ ? "gasto e receita em moedas diferentes"
70
+ : row.spendCents === null
71
+ ? noSpend
72
+ : "sem gasto no período";
73
+ return (_jsxs("section", { className: "rastro-portfolio__kpis", "aria-label": "Totais da carteira", children: [_jsx(Kpi, { label: "Gasto", value: spendCurrency === null
74
+ ? "—"
75
+ : formatMoney(row.spendCents, spendCurrency), delta: before && spendCurrency !== null
76
+ ? { label: change(row.spendCents, before.spendCents).label }
77
+ : undefined, sub: spendSub }), _jsx(Kpi, { label: "Leads", value: row.leads.toLocaleString("pt-BR"), delta: before ? delta(row.leads, before.leads) : undefined, sub: [
78
+ per(metrics.costPerLeadCents, "lead"),
79
+ `${row.qualified.toLocaleString("pt-BR")} qualificados`,
80
+ ]
81
+ .filter(Boolean)
82
+ .join(" · ") }), _jsx(Kpi, { label: "Vendas", value: row.won.toLocaleString("pt-BR"), delta: before ? delta(row.won, before.won) : undefined, sub: per(metrics.costPerSaleCents, "venda") ?? undefined }), _jsx(Kpi, { label: "Receita", value: formatMoney(row.revenueCents, currency), delta: before ? delta(row.revenueCents, before.revenueCents) : undefined, sub: "das vendas que os an\u00FAncios trouxeram" }), _jsx(Kpi, { highlight: true, label: "Retorno", value: formatReturn(metrics.returnOnSpend), delta: returnDelta
83
+ ? {
84
+ label: returnDelta,
85
+ trend: returnDelta.startsWith("+")
86
+ ? "good"
87
+ : returnDelta.startsWith("−")
88
+ ? "bad"
89
+ : undefined,
90
+ }
91
+ : undefined, sub: returnSub })] }));
92
+ }
93
+ const SPARK_WIDTH = 112;
94
+ const SPARK_HEIGHT = 32;
95
+ /** With this few sales a curve lies; one mark per sale tells it straight. */
96
+ const SPARK_MARKS_UNTIL = 5;
97
+ function Sparkline({ daily }) {
98
+ const days = [...daily].sort((a, b) => a.dayStart - b.dayStart);
99
+ const total = days.reduce((sum, day) => sum + day.won, 0);
100
+ const last = Math.max(days.length - 1, 1);
101
+ const x = (index) => 2 + (index / last) * (SPARK_WIDTH - 4);
102
+ const label = `${total === 1 ? "1 venda" : `${total} vendas`} em ${days.length} dias`;
103
+ if (total <= SPARK_MARKS_UNTIL) {
104
+ return (_jsxs("svg", { className: "rastro-portfolio__spark", width: SPARK_WIDTH, height: SPARK_HEIGHT, viewBox: `0 0 ${SPARK_WIDTH} ${SPARK_HEIGHT}`, role: "img", "aria-label": label, children: [_jsx("line", { className: "rastro-portfolio__spark-base", x1: 0, x2: SPARK_WIDTH, y1: SPARK_HEIGHT - 1, y2: SPARK_HEIGHT - 1 }), days.map((day, index) => day.won > 0 ? (_jsxs("g", { children: [_jsx("line", { className: "rastro-portfolio__spark-stem", x1: x(index), x2: x(index), y1: SPARK_HEIGHT - 1, y2: 9 }), _jsx("circle", { className: "rastro-portfolio__spark-dot", cx: x(index), cy: 7, r: 3 })] }, day.dayStart)) : null)] }));
105
+ }
106
+ // A 7-day average reads as a trend; single days are too jumpy.
107
+ const smooth = days.map((_, index) => {
108
+ const window = days.slice(Math.max(0, index - 6), index + 1);
109
+ return window.reduce((sum, day) => sum + day.won, 0) / window.length;
110
+ });
111
+ const max = Math.max(...smooth);
112
+ const min = Math.min(...smooth);
113
+ const y = (value) => SPARK_HEIGHT - 3 - ((value - min) / (max - min || 1)) * (SPARK_HEIGHT - 8);
114
+ const line = smooth
115
+ .map((value, index) => `${index ? "L" : "M"}${x(index).toFixed(1)} ${y(value).toFixed(1)}`)
116
+ .join("");
117
+ const endX = x(smooth.length - 1);
118
+ const endY = y(smooth.at(-1) ?? 0);
119
+ return (_jsxs("svg", { className: "rastro-portfolio__spark", width: SPARK_WIDTH, height: SPARK_HEIGHT, viewBox: `0 0 ${SPARK_WIDTH} ${SPARK_HEIGHT}`, role: "img", "aria-label": label, children: [_jsx("path", { className: "rastro-portfolio__spark-area", d: `${line}L${endX} ${SPARK_HEIGHT}L${x(0)} ${SPARK_HEIGHT}Z` }), _jsx("path", { className: "rastro-portfolio__spark-line", d: line }), _jsx("circle", { className: "rastro-portfolio__spark-dot", cx: endX, cy: endY, r: 2.5 })] }));
120
+ }
121
+ function ClientCard({ client, portfolio, daily, onOpen, }) {
122
+ const { site, metrics, spendCents } = client;
123
+ const insight = clientInsight(client, portfolio);
124
+ const band = returnBand(metrics.returnOnSpend);
125
+ const money = (cents) => formatMoney(cents, client.spendCurrency);
126
+ const figures = [
127
+ ["Gasto", money(spendCents)],
128
+ ["Leads", client.leads.toLocaleString("pt-BR")],
129
+ ["Custo por lead", money(metrics.costPerLeadCents)],
130
+ ["Vendas", client.won.toLocaleString("pt-BR")],
131
+ ["Receita", formatMoney(client.revenueCents, portfolio.currency)],
132
+ ["Custo por venda", money(metrics.costPerSaleCents)],
133
+ ];
134
+ return (_jsxs("article", { className: "rastro-card rastro-portfolio__client", "data-best": insight.kind === "bestReturn" ? "" : undefined, "aria-label": site.name, children: [_jsxs("header", { className: "rastro-portfolio__client-head", children: [_jsx("span", { className: "rastro-portfolio__avatar", "data-hue": hueSlot(site.siteId), "aria-hidden": "true", children: initials(site.name) }), _jsxs("div", { className: "rastro-portfolio__client-name", children: [_jsx("strong", { title: site.name, children: site.name }), site.segment && _jsx("span", { className: "rastro-muted", children: site.segment })] })] }), _jsxs("div", { className: "rastro-portfolio__return", children: [_jsxs("div", { children: [_jsx("span", { className: "rastro-portfolio__return-label", children: "Retorno" }), _jsx("strong", { className: "rastro-num rastro-portfolio__return-value", "data-band": band ?? undefined, children: formatReturn(metrics.returnOnSpend) })] }), daily && daily.length > 0 && (_jsxs("div", { className: "rastro-portfolio__spark-box", children: [_jsx(Sparkline, { daily: daily }), _jsxs("span", { className: "rastro-muted", children: ["vendas \u00B7 ", daily.length, " dias"] })] }))] }), _jsx("dl", { className: "rastro-portfolio__figures", children: figures.map(([label, value]) => (_jsxs("div", { children: [_jsx("dt", { children: label }), _jsx("dd", { className: "rastro-num", children: value })] }, label))) }), _jsx("p", { className: "rastro-portfolio__insight", "data-tone": insight.tone, children: insight.text }), _jsxs("button", { type: "button", className: "rastro-portfolio__open", onClick: onOpen, "aria-label": `Abrir campanhas de ${site.name}`, children: ["Abrir campanhas ", _jsx("span", { "aria-hidden": "true", children: "\u2192" })] })] }));
135
+ }
136
+ function ChartCard({ portfolio }) {
137
+ const [mode, setMode] = useState("return");
138
+ const { spendCurrency, currency } = portfolio;
139
+ // The chart compares spend in one currency: the clients the totals count.
140
+ const withSpend = portfolio.clients.filter((client) => spendState(client, portfolio) === "counted");
141
+ const outside = (state) => portfolio.clients
142
+ .filter((client) => spendState(client, portfolio) === state)
143
+ .map((client) => client.site.name);
144
+ const notes = [
145
+ ["syncing", "Sincronizando com a Meta"],
146
+ ["notConnected", "Meta não conectada"],
147
+ ["otherCurrency", "Gasto em outra moeda"],
148
+ ]
149
+ .map(([state, label]) => [label, outside(state)])
150
+ .filter(([, names]) => names.length > 0);
151
+ // Without a return (spend billed in another currency) the only honest
152
+ // comparison is cost per sale.
153
+ const canReturn = spendCurrency === currency && !portfolio.paid.currencyMismatch;
154
+ const active = canReturn ? mode : "costPerSale";
155
+ const ranked = [...withSpend]
156
+ .filter((client) => active === "return"
157
+ ? client.metrics.returnOnSpend !== null
158
+ : client.metrics.costPerSaleCents !== null)
159
+ .sort((a, b) => active === "return"
160
+ ? b.metrics.returnOnSpend - a.metrics.returnOnSpend
161
+ : a.metrics.costPerSaleCents - b.metrics.costPerSaleCents);
162
+ return (_jsxs("section", { className: "rastro-card rastro-portfolio__chart-card", "aria-label": "Gasto \u00D7 Retorno por cliente", children: [_jsxs("header", { className: "rastro-portfolio__section-head", children: [_jsxs("div", { children: [_jsxs("h3", { children: [active === "return"
163
+ ? "Gasto × Retorno"
164
+ : "Gasto × Custo por venda", " ", "por cliente"] }), _jsxs("p", { className: "rastro-muted", children: [active === "return"
165
+ ? "Quanto mais alto, mais cada real gasto volta em venda"
166
+ : "Quanto mais baixo, mais barato sai cada venda", " ", "\u00B7 bolha = receita"] })] }), canReturn && (_jsx("div", { className: "rastro-portfolio__toggle", role: "group", "aria-label": "Eixo do gr\u00E1fico", children: [
167
+ ["return", "Retorno"],
168
+ ["costPerSale", "Custo por venda"],
169
+ ].map(([value, label]) => (_jsx("button", { type: "button", "aria-pressed": mode === value, onClick: () => setMode(value), children: label }, value))) }))] }), spendCurrency === null ? (_jsx("p", { className: "rastro-empty", children: "As contas de an\u00FAncio cobram em moedas diferentes: o gasto de um cliente n\u00E3o se compara com o de outro." })) : ranked.length === 0 ? (_jsx("p", { className: "rastro-empty", children: "Nenhum cliente com gasto e vendas no per\u00EDodo." })) : (_jsxs(_Fragment, { children: [_jsx(SpendChart, { portfolio: portfolio, spendCurrency: spendCurrency, mode: active }), _jsxs("div", { className: "rastro-portfolio__rank", children: [_jsx("p", { className: "rastro-portfolio__rank-title", children: active === "return" ? "Volta por real gasto" : "Custo por venda" }), _jsx("ol", { children: ranked.map((client, index) => (_jsxs("li", { children: [_jsx("span", { className: "rastro-portfolio__rank-pos", children: index + 1 }), _jsx("span", { className: "rastro-portfolio__rank-name", children: client.site.name }), _jsx("strong", { className: "rastro-num", children: active === "return"
170
+ ? formatMoney(Math.round(client.metrics.returnOnSpend * 100), currency)
171
+ : formatMoney(client.metrics.costPerSaleCents, spendCurrency) })] }, client.site.siteId))) })] })] })), notes.map(([label, names]) => (_jsxs("p", { className: "rastro-portfolio__note rastro-muted", children: [label, ": ", names.join(", "), "."] }, label)))] }));
172
+ }
173
+ /** Alerts shown before "+N" asks to open the rest. */
174
+ const ALERTS_SHOWN = 5;
175
+ function Attention({ alerts, onOpenSite, }) {
176
+ const [expanded, setExpanded] = useState(false);
177
+ const shown = expanded ? alerts : alerts.slice(0, ALERTS_SHOWN);
178
+ const hidden = alerts.length - shown.length;
179
+ return (_jsxs("section", { className: "rastro-card rastro-portfolio__attention", "aria-label": "Precisa de aten\u00E7\u00E3o", children: [_jsxs("header", { className: "rastro-portfolio__section-head", children: [_jsxs("div", { children: [_jsx("h3", { children: "Precisa de aten\u00E7\u00E3o" }), _jsx("p", { className: "rastro-muted", children: "O que mudou e o que d\u00E1 pra fazer" })] }), alerts.length > 0 && (_jsx("span", { className: "rastro-num rastro-portfolio__count", "aria-label": `${alerts.length} alertas`, children: alerts.length }))] }), alerts.length === 0 ? (_jsx("p", { className: "rastro-empty", children: "Nada pedindo aten\u00E7\u00E3o agora." })) : (_jsx("ul", { className: "rastro-portfolio__alerts", children: shown.map((alert) => (_jsxs("li", { className: "rastro-portfolio__alert", "data-tone": alert.tone, children: [_jsxs("p", { children: [_jsxs("strong", { children: [alert.siteName, ":"] }), " ", alert.headline] }), _jsx("p", { className: "rastro-muted", children: alert.detail }), _jsxs("button", { type: "button", className: "rastro-portfolio__open", onClick: () => onOpenSite(alert.siteId, alert.adId ? { adId: alert.adId } : undefined), children: [alert.action, " ", _jsx("span", { "aria-hidden": "true", children: "\u2192" })] })] }, alert.id))) })), hidden > 0 && (_jsxs("button", { type: "button", className: "rastro-portfolio__more", onClick: () => setExpanded(true), "aria-label": `Ver mais ${hidden} alertas`, children: ["+", hidden] }))] }));
180
+ }
181
+ function Loading() {
182
+ return (_jsxs("div", { className: "rastro-ui rastro-portfolio", role: "status", "aria-busy": "true", "aria-label": "Carregando clientes", children: [_jsx("div", { className: "rastro-portfolio__kpis", children: Array.from({ length: 5 }, (_, index) => (_jsx("div", { className: "rastro-skeleton rastro-portfolio__kpi-skeleton" }, index))) }), _jsx("div", { className: "rastro-portfolio__clients", children: Array.from({ length: 4 }, (_, index) => (_jsx("div", { className: "rastro-skeleton rastro-portfolio__client-skeleton" }, index))) })] }));
183
+ }
184
+ /**
185
+ * The agency's home: every client from spend to sale. Totals of the leads the
186
+ * ads bought, a card per client, spend against return, and what needs a look.
187
+ */
188
+ export function PortfolioView({ sites, report, previous, dailyBySite, status, onOpenSite, now, loading, }) {
189
+ if (loading || !report)
190
+ return _jsx(Loading, {});
191
+ const portfolio = portfolioFigures({ sites, report, status });
192
+ if (portfolio.clients.length === 0) {
193
+ return (_jsx("div", { className: "rastro-ui rastro-portfolio", children: _jsxs("div", { className: "rastro-card rastro-empty", children: [_jsx("strong", { children: "Nenhum cliente com leads no per\u00EDodo" }), _jsx("span", { children: report.bySite
194
+ ? "Quando um anúncio trouxer um lead, o cliente aparece aqui."
195
+ : "Este host ainda não separa os leads por cliente: atualize o Rastro." })] }) }));
196
+ }
197
+ const previousFigures = previous
198
+ ? portfolioFigures({ sites, report: previous, status })
199
+ : null;
200
+ const alerts = portfolioAlerts({ sites, report, dailyBySite, status });
201
+ const accounts = (status ?? []).reduce((sum, site) => sum + site.adAccounts.length, 0);
202
+ const { paid, spendCurrency, currency } = portfolio;
203
+ const byState = (state) => portfolio.clients.filter((client) => spendState(client, portfolio) === state);
204
+ const counted = byState("counted").length;
205
+ const otherCurrency = byState("otherCurrency");
206
+ const reasons = [
207
+ [byState("syncing"), "sincronizando com a Meta"],
208
+ [byState("notConnected"), "sem a Meta conectada"],
209
+ [otherCurrency, "com gasto em outra moeda"],
210
+ ];
211
+ return (_jsxs("div", { className: "rastro-ui rastro-portfolio", children: [spendCurrency === null ? (_jsx("p", { className: "rastro-portfolio__warning", role: "note", children: "As contas de an\u00FAncio cobram em moedas diferentes: o gasto de cada cliente aparece na moeda da conta dele, sem soma nem retorno da carteira." })) : paid.currencyMismatch ? (_jsxs("p", { className: "rastro-portfolio__warning", role: "note", children: ["O gasto est\u00E1 em ", spendCurrency, " e as vendas em ", currency, ": o gasto aparece, o retorno n\u00E3o \u00E9 calculado."] })) : (otherCurrency.length > 0 && (_jsxs("p", { className: "rastro-portfolio__warning", role: "note", children: [otherCurrency.map((client) => client.site.name).join(", "), " ", otherCurrency.length === 1 ? "gasta" : "gastam", " em outra moeda:", " ", otherCurrency.length === 1 ? "fica" : "ficam", " fora dos totais e sem retorno."] }))), _jsx(Kpis, { portfolio: portfolio, previous: previousFigures, accounts: accounts, synced: syncedAgo(status, now) }), paid.partial && counted > 0 && (_jsxs("p", { className: "rastro-portfolio__partial rastro-muted", children: ["Calculado sobre ", counted, " de ", portfolio.clients.length, " clientes:", " ", reasons
212
+ .filter(([clients]) => clients.length > 0)
213
+ .map(([clients, reason]) => `${clients.map((client) => client.site.name).join(", ")} ${reason}`)
214
+ .join("; "), " ", portfolio.clients.length - counted === 1 ? "fica" : "ficam", " fora dos totais."] })), portfolio.spendIncomplete && (_jsx("p", { className: "rastro-portfolio__partial rastro-muted", role: "note", children: "Gasto por an\u00FAncio indispon\u00EDvel neste per\u00EDodo: os totais por cliente seguem certos, mas o an\u00FAncio que mais vende fica sem retorno. Escolha um per\u00EDodo menor para v\u00EA-lo." })), _jsx("section", { className: "rastro-portfolio__clients", "aria-label": "Clientes", children: portfolio.clients.map((client) => (_jsx(ClientCard, { client: client, portfolio: portfolio, daily: dailyBySite?.[client.site.siteId], onOpen: () => onOpenSite(client.site.siteId) }, client.site.siteId))) }), _jsxs("div", { className: "rastro-portfolio__bottom", children: [_jsx(ChartCard, { portfolio: portfolio }), _jsx(Attention, { alerts: alerts, onOpenSite: onOpenSite })] })] }));
215
+ }
216
+ //# sourceMappingURL=PortfolioView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortfolioView.js","sourceRoot":"","sources":["../../../src/ui/portfolio/PortfolioView.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type PortfolioFigures } from "./figures.js";
2
+ export type SpendChartMode = "return" | "costPerSale";
3
+ /** Spend against return (or cost per sale), one bubble per client. */
4
+ export declare function SpendChart({ portfolio, spendCurrency, mode, }: {
5
+ portfolio: PortfolioFigures;
6
+ /** The one currency every plotted account bills in. */
7
+ spendCurrency: string;
8
+ mode: SpendChartMode;
9
+ }): import("react").JSX.Element;
10
+ //# sourceMappingURL=SpendChart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpendChart.d.ts","sourceRoot":"","sources":["../../../src/ui/portfolio/SpendChart.tsx"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AAGtB,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,aAAa,CAAC;AA6DtD,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,aAAa,EACb,IAAI,GACL,EAAE;IACD,SAAS,EAAE,gBAAgB,CAAC;IAC5B,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,cAAc,CAAC;CACtB,+BAmTA"}