@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.
- package/README.md +9 -1
- package/agent/integration.md +23 -1
- package/agent/manifest.json +4 -4
- package/dist/client/federation.d.ts +114 -9
- package/dist/client/federation.d.ts.map +1 -1
- package/dist/client/federation.js +54 -2
- package/dist/client/federation.js.map +1 -1
- package/dist/client/index.d.ts +642 -8
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +391 -9
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +14 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +278 -1
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +4 -0
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/constants.d.ts +5 -0
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +10 -0
- package/dist/component/constants.js.map +1 -1
- package/dist/component/convex.config.d.ts +4 -0
- package/dist/component/convex.config.js +9 -0
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/errors.d.ts +1 -1
- package/dist/component/errors.d.ts.map +1 -1
- package/dist/component/errors.js.map +1 -1
- package/dist/component/leads.d.ts +69 -3
- package/dist/component/leads.d.ts.map +1 -1
- package/dist/component/leads.js +419 -16
- package/dist/component/leads.js.map +1 -1
- package/dist/component/meta.d.ts +358 -0
- package/dist/component/meta.d.ts.map +1 -0
- package/dist/component/meta.js +1295 -0
- package/dist/component/meta.js.map +1 -0
- package/dist/component/metaCapi.d.ts +162 -0
- package/dist/component/metaCapi.d.ts.map +1 -0
- package/dist/component/metaCapi.js +754 -0
- package/dist/component/metaCapi.js.map +1 -0
- package/dist/component/metaFake.d.ts +5 -0
- package/dist/component/metaFake.d.ts.map +1 -0
- package/dist/component/metaFake.js +225 -0
- package/dist/component/metaFake.js.map +1 -0
- package/dist/component/metaGraph.d.ts +165 -0
- package/dist/component/metaGraph.d.ts.map +1 -0
- package/dist/component/metaGraph.js +507 -0
- package/dist/component/metaGraph.js.map +1 -0
- package/dist/component/metaSync.d.ts +186 -0
- package/dist/component/metaSync.d.ts.map +1 -0
- package/dist/component/metaSync.js +961 -0
- package/dist/component/metaSync.js.map +1 -0
- package/dist/component/pipeline.d.ts +99 -0
- package/dist/component/pipeline.d.ts.map +1 -0
- package/dist/component/pipeline.js +337 -0
- package/dist/component/pipeline.js.map +1 -0
- package/dist/component/schema.d.ts +404 -2
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +168 -2
- package/dist/component/schema.js.map +1 -1
- package/dist/component/secrets.d.ts +15 -0
- package/dist/component/secrets.d.ts.map +1 -0
- package/dist/component/secrets.js +67 -0
- package/dist/component/secrets.js.map +1 -0
- package/dist/component/validators.d.ts +1052 -15
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +307 -3
- package/dist/component/validators.js.map +1 -1
- package/dist/tracker/generated.d.ts +1 -1
- package/dist/tracker/generated.d.ts.map +1 -1
- package/dist/tracker/generated.js +1 -1
- package/dist/tracker/generated.js.map +1 -1
- package/dist/ui/campaigns/CampaignsView.d.ts +48 -0
- package/dist/ui/campaigns/CampaignsView.d.ts.map +1 -0
- package/dist/ui/campaigns/CampaignsView.js +47 -0
- package/dist/ui/campaigns/CampaignsView.js.map +1 -0
- package/dist/ui/campaigns/Conversions.d.ts +16 -0
- package/dist/ui/campaigns/Conversions.d.ts.map +1 -0
- package/dist/ui/campaigns/Conversions.js +40 -0
- package/dist/ui/campaigns/Conversions.js.map +1 -0
- package/dist/ui/campaigns/Drawer.d.ts +24 -0
- package/dist/ui/campaigns/Drawer.d.ts.map +1 -0
- package/dist/ui/campaigns/Drawer.js +120 -0
- package/dist/ui/campaigns/Drawer.js.map +1 -0
- package/dist/ui/campaigns/Icons.d.ts +35 -0
- package/dist/ui/campaigns/Icons.d.ts.map +1 -0
- package/dist/ui/campaigns/Icons.js +41 -0
- package/dist/ui/campaigns/Icons.js.map +1 -0
- package/dist/ui/campaigns/Summary.d.ts +21 -0
- package/dist/ui/campaigns/Summary.d.ts.map +1 -0
- package/dist/ui/campaigns/Summary.js +56 -0
- package/dist/ui/campaigns/Summary.js.map +1 -0
- package/dist/ui/campaigns/Thumbnail.d.ts +11 -0
- package/dist/ui/campaigns/Thumbnail.d.ts.map +1 -0
- package/dist/ui/campaigns/Thumbnail.js +23 -0
- package/dist/ui/campaigns/Thumbnail.js.map +1 -0
- package/dist/ui/campaigns/Tree.d.ts +12 -0
- package/dist/ui/campaigns/Tree.d.ts.map +1 -0
- package/dist/ui/campaigns/Tree.js +105 -0
- package/dist/ui/campaigns/Tree.js.map +1 -0
- package/dist/ui/campaigns/glyphs.d.ts +20 -0
- package/dist/ui/campaigns/glyphs.d.ts.map +1 -0
- package/dist/ui/campaigns/glyphs.js +19 -0
- package/dist/ui/campaigns/glyphs.js.map +1 -0
- package/dist/ui/campaigns/index.d.ts +4 -0
- package/dist/ui/campaigns/index.d.ts.map +1 -0
- package/dist/ui/campaigns/index.js +3 -0
- package/dist/ui/campaigns/index.js.map +1 -0
- package/dist/ui/campaigns/labels.d.ts +53 -0
- package/dist/ui/campaigns/labels.d.ts.map +1 -0
- package/dist/ui/campaigns/labels.js +169 -0
- package/dist/ui/campaigns/labels.js.map +1 -0
- package/dist/ui/campaigns/model.d.ts +99 -0
- package/dist/ui/campaigns/model.d.ts.map +1 -0
- package/dist/ui/campaigns/model.js +230 -0
- package/dist/ui/campaigns/model.js.map +1 -0
- package/dist/ui/campaigns/modes.d.ts +18 -0
- package/dist/ui/campaigns/modes.d.ts.map +1 -0
- package/dist/ui/campaigns/modes.js +7 -0
- package/dist/ui/campaigns/modes.js.map +1 -0
- package/dist/ui/index.d.ts +21 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +20 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/integrations/AccountPicker.d.ts +26 -0
- package/dist/ui/integrations/AccountPicker.d.ts.map +1 -0
- package/dist/ui/integrations/AccountPicker.js +101 -0
- package/dist/ui/integrations/AccountPicker.js.map +1 -0
- package/dist/ui/integrations/ClientCards.d.ts +17 -0
- package/dist/ui/integrations/ClientCards.d.ts.map +1 -0
- package/dist/ui/integrations/ClientCards.js +42 -0
- package/dist/ui/integrations/ClientCards.js.map +1 -0
- package/dist/ui/integrations/ClientsTable.d.ts +36 -0
- package/dist/ui/integrations/ClientsTable.d.ts.map +1 -0
- package/dist/ui/integrations/ClientsTable.js +143 -0
- package/dist/ui/integrations/ClientsTable.js.map +1 -0
- package/dist/ui/integrations/Connect.d.ts +35 -0
- package/dist/ui/integrations/Connect.d.ts.map +1 -0
- package/dist/ui/integrations/Connect.js +62 -0
- package/dist/ui/integrations/Connect.js.map +1 -0
- package/dist/ui/integrations/Connection.d.ts +37 -0
- package/dist/ui/integrations/Connection.d.ts.map +1 -0
- package/dist/ui/integrations/Connection.js +108 -0
- package/dist/ui/integrations/Connection.js.map +1 -0
- package/dist/ui/integrations/Dialogs.d.ts +21 -0
- package/dist/ui/integrations/Dialogs.d.ts.map +1 -0
- package/dist/ui/integrations/Dialogs.js +34 -0
- package/dist/ui/integrations/Dialogs.js.map +1 -0
- package/dist/ui/integrations/EventsFeed.d.ts +15 -0
- package/dist/ui/integrations/EventsFeed.d.ts.map +1 -0
- package/dist/ui/integrations/EventsFeed.js +61 -0
- package/dist/ui/integrations/EventsFeed.js.map +1 -0
- package/dist/ui/integrations/EventsSheet.d.ts +34 -0
- package/dist/ui/integrations/EventsSheet.d.ts.map +1 -0
- package/dist/ui/integrations/EventsSheet.js +136 -0
- package/dist/ui/integrations/EventsSheet.js.map +1 -0
- package/dist/ui/integrations/Icons.d.ts +38 -0
- package/dist/ui/integrations/Icons.d.ts.map +1 -0
- package/dist/ui/integrations/Icons.js +40 -0
- package/dist/ui/integrations/Icons.js.map +1 -0
- package/dist/ui/integrations/IntegrationsView.d.ts +15 -0
- package/dist/ui/integrations/IntegrationsView.d.ts.map +1 -0
- package/dist/ui/integrations/IntegrationsView.js +141 -0
- package/dist/ui/integrations/IntegrationsView.js.map +1 -0
- package/dist/ui/integrations/Overlay.d.ts +18 -0
- package/dist/ui/integrations/Overlay.d.ts.map +1 -0
- package/dist/ui/integrations/Overlay.js +36 -0
- package/dist/ui/integrations/Overlay.js.map +1 -0
- package/dist/ui/integrations/Partner.d.ts +14 -0
- package/dist/ui/integrations/Partner.d.ts.map +1 -0
- package/dist/ui/integrations/Partner.js +38 -0
- package/dist/ui/integrations/Partner.js.map +1 -0
- package/dist/ui/integrations/RowMenu.d.ts +15 -0
- package/dist/ui/integrations/RowMenu.d.ts.map +1 -0
- package/dist/ui/integrations/RowMenu.js +47 -0
- package/dist/ui/integrations/RowMenu.js.map +1 -0
- package/dist/ui/integrations/UnlinkDialog.d.ts +18 -0
- package/dist/ui/integrations/UnlinkDialog.d.ts.map +1 -0
- package/dist/ui/integrations/UnlinkDialog.js +29 -0
- package/dist/ui/integrations/UnlinkDialog.js.map +1 -0
- package/dist/ui/integrations/audience.d.ts +12 -0
- package/dist/ui/integrations/audience.d.ts.map +1 -0
- package/dist/ui/integrations/audience.js +12 -0
- package/dist/ui/integrations/audience.js.map +1 -0
- package/dist/ui/integrations/format.d.ts +28 -0
- package/dist/ui/integrations/format.d.ts.map +1 -0
- package/dist/ui/integrations/format.js +101 -0
- package/dist/ui/integrations/format.js.map +1 -0
- package/dist/ui/integrations/index.d.ts +5 -0
- package/dist/ui/integrations/index.d.ts.map +1 -0
- package/dist/ui/integrations/index.js +3 -0
- package/dist/ui/integrations/index.js.map +1 -0
- package/dist/ui/integrations/modal.d.ts +10 -0
- package/dist/ui/integrations/modal.d.ts.map +1 -0
- package/dist/ui/integrations/modal.js +77 -0
- package/dist/ui/integrations/modal.js.map +1 -0
- package/dist/ui/integrations/model.d.ts +99 -0
- package/dist/ui/integrations/model.d.ts.map +1 -0
- package/dist/ui/integrations/model.js +164 -0
- package/dist/ui/integrations/model.js.map +1 -0
- package/dist/ui/integrations/parts.d.ts +36 -0
- package/dist/ui/integrations/parts.d.ts.map +1 -0
- package/dist/ui/integrations/parts.js +85 -0
- package/dist/ui/integrations/parts.js.map +1 -0
- package/dist/ui/integrations/types.d.ts +146 -0
- package/dist/ui/integrations/types.d.ts.map +1 -0
- package/dist/ui/integrations/types.js +2 -0
- package/dist/ui/integrations/types.js.map +1 -0
- package/dist/ui/metrics.d.ts +106 -0
- package/dist/ui/metrics.d.ts.map +1 -0
- package/dist/ui/metrics.js +166 -0
- package/dist/ui/metrics.js.map +1 -0
- package/dist/ui/portfolio/PortfolioView.d.ts +26 -0
- package/dist/ui/portfolio/PortfolioView.d.ts.map +1 -0
- package/dist/ui/portfolio/PortfolioView.js +216 -0
- package/dist/ui/portfolio/PortfolioView.js.map +1 -0
- package/dist/ui/portfolio/SpendChart.d.ts +10 -0
- package/dist/ui/portfolio/SpendChart.d.ts.map +1 -0
- package/dist/ui/portfolio/SpendChart.js +196 -0
- package/dist/ui/portfolio/SpendChart.js.map +1 -0
- package/dist/ui/portfolio/alerts.d.ts +74 -0
- package/dist/ui/portfolio/alerts.d.ts.map +1 -0
- package/dist/ui/portfolio/alerts.js +291 -0
- package/dist/ui/portfolio/alerts.js.map +1 -0
- package/dist/ui/portfolio/figures.d.ts +79 -0
- package/dist/ui/portfolio/figures.d.ts.map +1 -0
- package/dist/ui/portfolio/figures.js +103 -0
- package/dist/ui/portfolio/figures.js.map +1 -0
- package/dist/ui/portfolio/format.d.ts +8 -0
- package/dist/ui/portfolio/format.d.ts.map +1 -0
- package/dist/ui/portfolio/format.js +28 -0
- package/dist/ui/portfolio/format.js.map +1 -0
- package/dist/ui/sample.d.ts +536 -0
- package/dist/ui/sample.d.ts.map +1 -0
- package/dist/ui/sample.js +574 -0
- package/dist/ui/sample.js.map +1 -0
- package/dist/ui/seed.d.ts +188 -0
- package/dist/ui/seed.d.ts.map +1 -0
- package/dist/ui/seed.js +563 -0
- package/dist/ui/seed.js.map +1 -0
- package/dist/ui/ui.css +3424 -0
- package/docs/federation.md +46 -1
- package/docs/meta-ads.md +795 -0
- package/docs/upgrading.md +116 -0
- package/llms.txt +3 -0
- package/package.json +15 -3
- package/src/component/_generated/api.ts +14 -0
- package/src/component/_generated/component.ts +324 -1
- package/src/component/_generated/server.ts +4 -0
- package/src/component/constants.ts +10 -0
- package/src/component/convex.config.ts +9 -0
- package/src/component/errors.ts +5 -1
- package/src/component/leads.ts +517 -17
- package/src/component/meta.ts +1525 -0
- package/src/component/metaCapi.ts +921 -0
- package/src/component/metaFake.ts +277 -0
- package/src/component/metaGraph.ts +722 -0
- package/src/component/metaSync.ts +1164 -0
- package/src/component/pipeline.ts +410 -0
- package/src/component/schema.ts +197 -1
- package/src/component/secrets.ts +98 -0
- package/src/component/validators.ts +376 -3
- package/src/tracker/generated.ts +1 -1
- 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"}
|