@iann29/rastro 0.9.0 → 0.10.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.
Files changed (257) 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 +637 -8
  9. package/dist/client/index.d.ts.map +1 -1
  10. package/dist/client/index.js +385 -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 +277 -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 +352 -0
  35. package/dist/component/meta.d.ts.map +1 -0
  36. package/dist/component/meta.js +1239 -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 +98 -0
  55. package/dist/component/pipeline.d.ts.map +1 -0
  56. package/dist/component/pipeline.js +324 -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 +19 -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 +97 -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 +38 -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 +127 -0
  137. package/dist/ui/integrations/ClientsTable.js.map +1 -0
  138. package/dist/ui/integrations/Connect.d.ts +29 -0
  139. package/dist/ui/integrations/Connect.d.ts.map +1 -0
  140. package/dist/ui/integrations/Connect.js +49 -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 +94 -0
  145. package/dist/ui/integrations/Connection.js.map +1 -0
  146. package/dist/ui/integrations/Dialogs.d.ts +18 -0
  147. package/dist/ui/integrations/Dialogs.d.ts.map +1 -0
  148. package/dist/ui/integrations/Dialogs.js +25 -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 +57 -0
  153. package/dist/ui/integrations/EventsFeed.js.map +1 -0
  154. package/dist/ui/integrations/EventsSheet.d.ts +28 -0
  155. package/dist/ui/integrations/EventsSheet.d.ts.map +1 -0
  156. package/dist/ui/integrations/EventsSheet.js +121 -0
  157. package/dist/ui/integrations/EventsSheet.js.map +1 -0
  158. package/dist/ui/integrations/Icons.d.ts +36 -0
  159. package/dist/ui/integrations/Icons.d.ts.map +1 -0
  160. package/dist/ui/integrations/Icons.js +38 -0
  161. package/dist/ui/integrations/Icons.js.map +1 -0
  162. package/dist/ui/integrations/IntegrationsView.d.ts +14 -0
  163. package/dist/ui/integrations/IntegrationsView.d.ts.map +1 -0
  164. package/dist/ui/integrations/IntegrationsView.js +113 -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/RowMenu.d.ts +15 -0
  171. package/dist/ui/integrations/RowMenu.d.ts.map +1 -0
  172. package/dist/ui/integrations/RowMenu.js +47 -0
  173. package/dist/ui/integrations/RowMenu.js.map +1 -0
  174. package/dist/ui/integrations/UnlinkDialog.d.ts +18 -0
  175. package/dist/ui/integrations/UnlinkDialog.d.ts.map +1 -0
  176. package/dist/ui/integrations/UnlinkDialog.js +29 -0
  177. package/dist/ui/integrations/UnlinkDialog.js.map +1 -0
  178. package/dist/ui/integrations/format.d.ts +28 -0
  179. package/dist/ui/integrations/format.d.ts.map +1 -0
  180. package/dist/ui/integrations/format.js +101 -0
  181. package/dist/ui/integrations/format.js.map +1 -0
  182. package/dist/ui/integrations/index.d.ts +5 -0
  183. package/dist/ui/integrations/index.d.ts.map +1 -0
  184. package/dist/ui/integrations/index.js +3 -0
  185. package/dist/ui/integrations/index.js.map +1 -0
  186. package/dist/ui/integrations/model.d.ts +84 -0
  187. package/dist/ui/integrations/model.d.ts.map +1 -0
  188. package/dist/ui/integrations/model.js +131 -0
  189. package/dist/ui/integrations/model.js.map +1 -0
  190. package/dist/ui/integrations/overlay.d.ts +10 -0
  191. package/dist/ui/integrations/overlay.d.ts.map +1 -0
  192. package/dist/ui/integrations/overlay.js +77 -0
  193. package/dist/ui/integrations/overlay.js.map +1 -0
  194. package/dist/ui/integrations/parts.d.ts +36 -0
  195. package/dist/ui/integrations/parts.d.ts.map +1 -0
  196. package/dist/ui/integrations/parts.js +85 -0
  197. package/dist/ui/integrations/parts.js.map +1 -0
  198. package/dist/ui/integrations/types.d.ts +96 -0
  199. package/dist/ui/integrations/types.d.ts.map +1 -0
  200. package/dist/ui/integrations/types.js +2 -0
  201. package/dist/ui/integrations/types.js.map +1 -0
  202. package/dist/ui/metrics.d.ts +106 -0
  203. package/dist/ui/metrics.d.ts.map +1 -0
  204. package/dist/ui/metrics.js +166 -0
  205. package/dist/ui/metrics.js.map +1 -0
  206. package/dist/ui/portfolio/PortfolioView.d.ts +26 -0
  207. package/dist/ui/portfolio/PortfolioView.d.ts.map +1 -0
  208. package/dist/ui/portfolio/PortfolioView.js +216 -0
  209. package/dist/ui/portfolio/PortfolioView.js.map +1 -0
  210. package/dist/ui/portfolio/SpendChart.d.ts +10 -0
  211. package/dist/ui/portfolio/SpendChart.d.ts.map +1 -0
  212. package/dist/ui/portfolio/SpendChart.js +196 -0
  213. package/dist/ui/portfolio/SpendChart.js.map +1 -0
  214. package/dist/ui/portfolio/alerts.d.ts +74 -0
  215. package/dist/ui/portfolio/alerts.d.ts.map +1 -0
  216. package/dist/ui/portfolio/alerts.js +291 -0
  217. package/dist/ui/portfolio/alerts.js.map +1 -0
  218. package/dist/ui/portfolio/figures.d.ts +79 -0
  219. package/dist/ui/portfolio/figures.d.ts.map +1 -0
  220. package/dist/ui/portfolio/figures.js +103 -0
  221. package/dist/ui/portfolio/figures.js.map +1 -0
  222. package/dist/ui/portfolio/format.d.ts +8 -0
  223. package/dist/ui/portfolio/format.d.ts.map +1 -0
  224. package/dist/ui/portfolio/format.js +28 -0
  225. package/dist/ui/portfolio/format.js.map +1 -0
  226. package/dist/ui/sample.d.ts +474 -0
  227. package/dist/ui/sample.d.ts.map +1 -0
  228. package/dist/ui/sample.js +553 -0
  229. package/dist/ui/sample.js.map +1 -0
  230. package/dist/ui/seed.d.ts +188 -0
  231. package/dist/ui/seed.d.ts.map +1 -0
  232. package/dist/ui/seed.js +563 -0
  233. package/dist/ui/seed.js.map +1 -0
  234. package/dist/ui/ui.css +3356 -0
  235. package/docs/federation.md +46 -1
  236. package/docs/meta-ads.md +746 -0
  237. package/docs/upgrading.md +95 -0
  238. package/llms.txt +3 -0
  239. package/package.json +15 -3
  240. package/src/component/_generated/api.ts +14 -0
  241. package/src/component/_generated/component.ts +323 -1
  242. package/src/component/_generated/server.ts +4 -0
  243. package/src/component/constants.ts +10 -0
  244. package/src/component/convex.config.ts +9 -0
  245. package/src/component/errors.ts +5 -1
  246. package/src/component/leads.ts +517 -17
  247. package/src/component/meta.ts +1461 -0
  248. package/src/component/metaCapi.ts +921 -0
  249. package/src/component/metaFake.ts +277 -0
  250. package/src/component/metaGraph.ts +722 -0
  251. package/src/component/metaSync.ts +1164 -0
  252. package/src/component/pipeline.ts +392 -0
  253. package/src/component/schema.ts +197 -1
  254. package/src/component/secrets.ts +98 -0
  255. package/src/component/validators.ts +376 -3
  256. package/src/tracker/generated.ts +1 -1
  257. package/src/ui/seed.ts +755 -0
@@ -0,0 +1,85 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { initials } from "../portfolio/format.js";
3
+ import { agoLabel, plural } from "./format.js";
4
+ import { Icon } from "./Icons.js";
5
+ import { backfillStep } from "./model.js";
6
+ /** A rounded status chip: a dot, a spinner or an icon, then its words. */
7
+ export function Pill({ tone, icon, children, }) {
8
+ return (_jsxs("span", { className: "rastro-integrations__pill", "data-tone": tone, children: [icon === "dot" ? (_jsx("i", { className: "rastro-integrations__dot", "aria-hidden": "true" })) : icon === "spin" ? (_jsx("i", { className: "rastro-integrations__spin", "aria-hidden": "true" })) : icon ? (_jsx(Icon, { name: icon, size: 12 })) : null, children] }));
9
+ }
10
+ export const TEST_MODE_TEXT = "Modo de teste: as vendas vão só para o Test Events da Meta";
11
+ /**
12
+ * The pixel has a test event code (`metaStatus.pixel.testMode`): what is sent
13
+ * shows up in Meta's Test Events and nowhere else. `line` is the client row's
14
+ * and the phone card's chip; the events sheet says it as a callout.
15
+ */
16
+ export function TestModeNote({ variant }) {
17
+ if (variant === "callout")
18
+ return (_jsxs("div", { className: "rastro-integrations__callout", "data-tone": "warn", role: "note", children: [_jsx(Icon, { name: "alert", size: 14 }), _jsxs("p", { children: [_jsx("b", { children: "Modo de teste:" }), " as vendas v\u00E3o s\u00F3 para o Test Events da Meta. Tire o c\u00F3digo de teste do pixel para elas contarem nos an\u00FAncios."] })] }));
19
+ return (_jsxs("span", { className: "rastro-integrations__test-mode", role: "note", title: TEST_MODE_TEXT, children: [_jsx(Pill, { tone: "warn", icon: "alert", children: "Modo de teste" }), _jsx("span", { className: "rastro-integrations__meta", children: "as vendas v\u00E3o s\u00F3 para o Test Events da Meta" })] }));
20
+ }
21
+ /** A client's initials in its color slot (the order the agency lists them). */
22
+ export function Avatar({ name, slot, size = "md", }) {
23
+ return (_jsx("span", { className: "rastro-integrations__avatar", "data-hue": slot % 6, "data-size": size, "aria-hidden": "true", children: initials(name) }));
24
+ }
25
+ /** The Estado chip: how the client's account syncs. */
26
+ export function StateChip({ row, now, compact = false, }) {
27
+ const step = backfillStep(row.account?.backfill);
28
+ const chunk = step ? `${step.current} de ${step.total}` : null;
29
+ switch (row.state) {
30
+ case "offline":
31
+ return _jsx(Pill, { tone: "ghost", children: "Meta n\u00E3o conectada" });
32
+ case "unlinked":
33
+ return _jsx(Pill, { tone: "ghost", children: "Sem conta de an\u00FAncio" });
34
+ case "planB":
35
+ return (_jsx(Pill, { tone: "ok", icon: "dot", children: "Pixel ok \u00B7 plano B" }));
36
+ case "syncing":
37
+ return (_jsx(Pill, { tone: "sync", icon: "spin", children: compact
38
+ ? chunk
39
+ ? `Sincronizando ${chunk}`
40
+ : "Sincronizando"
41
+ : chunk
42
+ ? `Sincronizando 90 dias · ${chunk}`
43
+ : "Sincronizando 90 dias" }));
44
+ case "error":
45
+ return (_jsx(Pill, { tone: "bad", icon: "alert", children: row.notGranted === "account"
46
+ ? compact
47
+ ? "Conta não liberada"
48
+ : "Conta não liberada nesta conexão"
49
+ : row.notGranted === "pixel"
50
+ ? compact
51
+ ? "Pixel não liberado"
52
+ : "Pixel não liberado nesta conexão"
53
+ : compact
54
+ ? "Erro"
55
+ : "Erro na leitura" }));
56
+ case "stopped":
57
+ return (_jsx(Pill, { tone: "bad", icon: "alert", children: row.account?.syncState === "backfill" && chunk
58
+ ? `Parado no bloco ${chunk}`
59
+ : compact
60
+ ? "Parado"
61
+ : "Parado · token recusado" }));
62
+ case "live":
63
+ return (_jsx(Pill, { tone: "ok", icon: "dot", children: compact
64
+ ? `Ao vivo · ${agoLabel(row.account.lastSyncAt, now, { short: true })}`
65
+ : `Ao vivo · ${agoLabel(row.account.lastSyncAt, now)}` }));
66
+ }
67
+ }
68
+ /** "41 vendas · 118 qualificados": what the pixel got in 30 days. */
69
+ export function sentSummary(row, { compact = false } = {}) {
70
+ const sent = row.sent30d;
71
+ if (!sent) {
72
+ return row.sent > 0 ? plural(row.sent, "enviado", "enviados") : null;
73
+ }
74
+ const sales = sent.Purchase ?? 0;
75
+ const qualified = sent.QualifiedLead ?? 0;
76
+ if (compact)
77
+ return plural(sales, "venda", "vendas");
78
+ return [
79
+ plural(sales, "venda", "vendas"),
80
+ qualified > 0 ? plural(qualified, "qualificado", "qualificados") : null,
81
+ ]
82
+ .filter(Boolean)
83
+ .join(" · ");
84
+ }
85
+ //# sourceMappingURL=parts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts.js","sourceRoot":"","sources":["../../../src/ui/integrations/parts.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The shapes the Integrações screen reads: the component's Meta reads
3
+ * (contract §11.1) re-exported from the client, plus the optional fields the
4
+ * approved artboards draw beyond them (marked "screen"). A host that does not
5
+ * send a screen field gets a plainer line, never a wrong one.
6
+ */
7
+ import type { EventMap, EventMapColumn, MetaColumnEvent, MetaConnection, MetaConnectionAdAccount, MetaConversion, MetaPixel, MetaPixelSource, MetaStatus, SetEventMapInput, UnlinkPreview } from "../../client/index.js";
8
+ export type { EventMap, EventMapColumn, MetaColumnEvent, MetaConnection, MetaConnectionAdAccount, MetaPixel, MetaPixelSource, SetEventMapInput, UnlinkPreview, };
9
+ /** Every event Meta receives: the columns' and the sale's Purchase. */
10
+ export type MetaEventName = MetaColumnEvent | "Purchase";
11
+ /**
12
+ * Screen: events the site's pixel got in the last 30 days, per name, and the
13
+ * failures ("41 vendas · 118 qualificados"). Not in §11.1 yet; without it the
14
+ * screen counts `meta.status` sends instead.
15
+ */
16
+ export type SentCounts = Partial<Record<MetaEventName, number>> & {
17
+ failed?: number;
18
+ /** Today (UTC, as the host counts it): the feed's "60 enviados hoje". */
19
+ today?: {
20
+ sent: number;
21
+ failed?: number;
22
+ };
23
+ };
24
+ /** A site's Meta status (`meta.status`) with the fields §11.1 adds. */
25
+ export type IntegrationStatus = Array<Omit<MetaStatus[number], "pixel" | "adAccounts"> & {
26
+ adAccounts: MetaStatus[number]["adAccounts"];
27
+ pixel: MetaStatus[number]["pixel"];
28
+ }>;
29
+ /** A conversion with the event names §11.1 adds (Schedule). */
30
+ export type IntegrationConversion = Omit<MetaConversion, "eventName"> & {
31
+ eventName: MetaEventName;
32
+ };
33
+ export interface IntegrationSite {
34
+ siteId: string;
35
+ name: string;
36
+ segment?: string;
37
+ }
38
+ /** The pixel to set: one the connection lists, or plan B with its own token. */
39
+ export type PixelChoice = {
40
+ pixelId: string;
41
+ } | {
42
+ pixelId: string;
43
+ accessToken: string;
44
+ };
45
+ /** The write in flight, so its control can say so. */
46
+ export type IntegrationsBusy = "connect" | "disconnect" | "link" | "unlink" | "pixel" | "events" | null;
47
+ export interface IntegrationsViewProps {
48
+ /** undefined while loading; null when the agency never connected Meta. */
49
+ connection: MetaConnection | null | undefined;
50
+ sites: readonly IntegrationSite[];
51
+ /** `meta.status` for the sites; undefined while loading. */
52
+ status: IntegrationStatus | undefined;
53
+ /** The latest conversions per site (`meta.conversions`). */
54
+ conversions?: Record<string, readonly IntegrationConversion[]> | undefined;
55
+ /**
56
+ * Older pages of a site's conversions: whether the host has more past the
57
+ * last row loaded, and whether a page is on its way. The events sheet's
58
+ * "Ver mais" asks for the next one with `onLoadOlderConversions`.
59
+ */
60
+ conversionPages?: Record<string, {
61
+ hasMore: boolean;
62
+ loading: boolean;
63
+ }> | undefined;
64
+ onLoadOlderConversions?: (siteId: string) => void;
65
+ /** Screen: ad names by id, for the feed's "Vídeo provador 15s". */
66
+ adNames?: Record<string, string> | undefined;
67
+ eventMaps?: Record<string, EventMap> | undefined;
68
+ /** Screen: 30-day sends per site (see `SentCounts`). */
69
+ sentBySite?: Record<string, SentCounts> | undefined;
70
+ /** Previews keyed `${siteId}:${adAccountId}`, asked for when the dialog opens. */
71
+ unlinkPreviews?: Record<string, UnlinkPreview | null | undefined> | undefined;
72
+ onRequestUnlinkPreview?: (siteId: string, adAccountId: string) => void;
73
+ /**
74
+ * Who links and sets what `metaStatus` marks `own: false` (another owner
75
+ * of the same site): "uze.ai" by default, as in "Ligado pelo uze.ai".
76
+ */
77
+ otherOwnerLabel?: string;
78
+ /** False for a reader without configure: the same screen, read only. */
79
+ permitted: boolean;
80
+ busy?: IntegrationsBusy;
81
+ /** The last write's refusal, in pt-BR. */
82
+ error?: string | null;
83
+ /** The last write's good news ("Meta conectada: 4 contas e 3 pixels…"). */
84
+ notice?: string | null;
85
+ onConnect: () => void;
86
+ onReconnect: () => void;
87
+ onChooseAssets: () => void;
88
+ onDisconnect: () => void;
89
+ onLinkAccount: (siteId: string, adAccountId: string) => void;
90
+ onUnlinkAccount: (siteId: string, adAccountId: string, replaceWith?: string) => void;
91
+ onSetPixel: (siteId: string, pixel: PixelChoice) => void;
92
+ onSaveEventMap: (siteId: string, map: Omit<SetEventMapInput, "siteId">) => void;
93
+ now: number;
94
+ loading?: boolean;
95
+ }
96
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/ui/integrations/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/ui/integrations/types.ts"],"names":[],"mappings":""}
@@ -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"}