@endora-commerce/mod-meta-ads 0.0.0-stage → 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -2
- package/dist/admin/api/meta-ads-client.d.ts +32 -0
- package/dist/admin/api/meta-ads-client.d.ts.map +1 -0
- package/dist/admin/api/meta-ads-client.js +34 -0
- package/dist/admin/api/meta-ads-client.js.map +1 -0
- package/dist/admin/index.d.ts +30 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +59 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CustomEventMappingEditPage.d.ts +4 -0
- package/dist/admin/pages/CustomEventMappingEditPage.d.ts.map +1 -0
- package/dist/admin/pages/CustomEventMappingEditPage.js +85 -0
- package/dist/admin/pages/CustomEventMappingEditPage.js.map +1 -0
- package/dist/admin/pages/CustomEventMappingsListPage.d.ts +7 -0
- package/dist/admin/pages/CustomEventMappingsListPage.d.ts.map +1 -0
- package/dist/admin/pages/CustomEventMappingsListPage.js +90 -0
- package/dist/admin/pages/CustomEventMappingsListPage.js.map +1 -0
- package/dist/backend/entities/meta-custom-event-mapping.entity.d.ts +24 -0
- package/dist/backend/entities/meta-custom-event-mapping.entity.d.ts.map +1 -0
- package/dist/backend/entities/meta-custom-event-mapping.entity.js +75 -0
- package/dist/backend/entities/meta-custom-event-mapping.entity.js.map +1 -0
- package/dist/backend/index.d.ts +67 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +56 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +16 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +31 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.storefront.d.ts +11 -0
- package/dist/backend/routes.storefront.d.ts.map +1 -0
- package/dist/backend/routes.storefront.js +13 -0
- package/dist/backend/routes.storefront.js.map +1 -0
- package/dist/backend/services/custom-event-mappings.service.d.ts +40 -0
- package/dist/backend/services/custom-event-mappings.service.d.ts.map +1 -0
- package/dist/backend/services/custom-event-mappings.service.js +134 -0
- package/dist/backend/services/custom-event-mappings.service.js.map +1 -0
- package/dist/backend/services/meta-config.service.d.ts +22 -0
- package/dist/backend/services/meta-config.service.d.ts.map +1 -0
- package/dist/backend/services/meta-config.service.js +55 -0
- package/dist/backend/services/meta-config.service.js.map +1 -0
- package/dist/manifest.d.ts +194 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +102 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260728T002715_meta_ads_init.d.ts +12 -0
- package/dist/migrations/20260728T002715_meta_ads_init.d.ts.map +1 -0
- package/dist/migrations/20260728T002715_meta_ads_init.js +35 -0
- package/dist/migrations/20260728T002715_meta_ads_init.js.map +1 -0
- package/dist/migrations/index.d.ts +27 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +29 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/meta-ads.md +82 -0
- package/i18n/en.json +35 -0
- package/i18n/pl.json +35 -0
- package/package.json +89 -3
- package/tailwind.css +14 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meta Ads module (feature 064). Puts the Meta Pixel on the storefront per sales
|
|
3
|
+
* channel, reports Meta's standard commerce events, and lets an operator add
|
|
4
|
+
* custom events alongside them.
|
|
5
|
+
*
|
|
6
|
+
* Per-channel configuration is stored through the Settings module. Server-side
|
|
7
|
+
* reporting through Meta's Conversions API is deliberately out of v1 — see
|
|
8
|
+
* specs/064-meta-ads/research.md §R7.
|
|
9
|
+
*/
|
|
10
|
+
export declare const metaAdsSettingsManifest: {
|
|
11
|
+
moduleCode: string;
|
|
12
|
+
groups: {
|
|
13
|
+
code: string;
|
|
14
|
+
name: string;
|
|
15
|
+
salesChannelCodes?: string[] | undefined;
|
|
16
|
+
isSystemProtected?: boolean | undefined;
|
|
17
|
+
}[];
|
|
18
|
+
settings: {
|
|
19
|
+
code: string;
|
|
20
|
+
name: string;
|
|
21
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
22
|
+
defaultValue: unknown;
|
|
23
|
+
description?: string | undefined;
|
|
24
|
+
groupCode?: string | undefined;
|
|
25
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
26
|
+
salesChannelCodes?: string[] | undefined;
|
|
27
|
+
enumOptions?: string[] | undefined;
|
|
28
|
+
configurationType?: string | undefined;
|
|
29
|
+
hidden?: boolean | undefined;
|
|
30
|
+
}[];
|
|
31
|
+
};
|
|
32
|
+
export declare const META_ADS_READ_PERMISSION = "meta_ads:read";
|
|
33
|
+
export declare const META_ADS_WRITE_PERMISSION = "meta_ads:write";
|
|
34
|
+
export declare const manifest: {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
version: string;
|
|
38
|
+
dependencies: string[];
|
|
39
|
+
description?: string | undefined;
|
|
40
|
+
acknowledgedDependencies?: {
|
|
41
|
+
moduleId: string;
|
|
42
|
+
port: string;
|
|
43
|
+
reason: string;
|
|
44
|
+
}[] | undefined;
|
|
45
|
+
nonBindingDependencies?: {
|
|
46
|
+
moduleId: string;
|
|
47
|
+
name: string;
|
|
48
|
+
kind: "contributes-to" | "degrades-without" | "refuses-without";
|
|
49
|
+
reason: string;
|
|
50
|
+
whenAbsent?: string | undefined;
|
|
51
|
+
}[] | undefined;
|
|
52
|
+
activation?: {
|
|
53
|
+
settingCode: string;
|
|
54
|
+
default: boolean;
|
|
55
|
+
} | {
|
|
56
|
+
nonDeactivatable: true;
|
|
57
|
+
reason: string;
|
|
58
|
+
} | undefined;
|
|
59
|
+
settings?: {
|
|
60
|
+
moduleCode: string;
|
|
61
|
+
groups: {
|
|
62
|
+
code: string;
|
|
63
|
+
name: string;
|
|
64
|
+
salesChannelCodes?: string[] | undefined;
|
|
65
|
+
isSystemProtected?: boolean | undefined;
|
|
66
|
+
}[];
|
|
67
|
+
settings: {
|
|
68
|
+
code: string;
|
|
69
|
+
name: string;
|
|
70
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
71
|
+
defaultValue: unknown;
|
|
72
|
+
description?: string | undefined;
|
|
73
|
+
groupCode?: string | undefined;
|
|
74
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
75
|
+
salesChannelCodes?: string[] | undefined;
|
|
76
|
+
enumOptions?: string[] | undefined;
|
|
77
|
+
configurationType?: string | undefined;
|
|
78
|
+
hidden?: boolean | undefined;
|
|
79
|
+
}[];
|
|
80
|
+
} | undefined;
|
|
81
|
+
i18n?: {
|
|
82
|
+
bundlesDir: string;
|
|
83
|
+
} | undefined;
|
|
84
|
+
docs?: false | {
|
|
85
|
+
dir: string;
|
|
86
|
+
} | undefined;
|
|
87
|
+
demo?: false | {
|
|
88
|
+
summary: string;
|
|
89
|
+
seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
|
|
90
|
+
reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
|
|
91
|
+
after?: readonly string[] | undefined;
|
|
92
|
+
package?: string | undefined;
|
|
93
|
+
} | undefined;
|
|
94
|
+
actions?: {
|
|
95
|
+
id: string;
|
|
96
|
+
labelKey: string;
|
|
97
|
+
icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
|
|
98
|
+
targetRoute: string;
|
|
99
|
+
keywords: string[];
|
|
100
|
+
weight: number;
|
|
101
|
+
descriptionKey?: string | undefined;
|
|
102
|
+
requiredPermission?: string | undefined;
|
|
103
|
+
}[] | undefined;
|
|
104
|
+
permissions?: {
|
|
105
|
+
code: string;
|
|
106
|
+
label: string;
|
|
107
|
+
module?: string | undefined;
|
|
108
|
+
description?: string | undefined;
|
|
109
|
+
requires?: string[] | undefined;
|
|
110
|
+
}[] | undefined;
|
|
111
|
+
transactionalEmails?: {
|
|
112
|
+
code: string;
|
|
113
|
+
name: string;
|
|
114
|
+
variables: {
|
|
115
|
+
key: string;
|
|
116
|
+
label: string;
|
|
117
|
+
sampleValue?: string | undefined;
|
|
118
|
+
description?: string | undefined;
|
|
119
|
+
}[];
|
|
120
|
+
description?: string | undefined;
|
|
121
|
+
group?: string | undefined;
|
|
122
|
+
}[] | undefined;
|
|
123
|
+
capabilities?: string[] | undefined;
|
|
124
|
+
exclusiveCapabilities?: {
|
|
125
|
+
key: string;
|
|
126
|
+
errorCode: string;
|
|
127
|
+
}[] | undefined;
|
|
128
|
+
errorCodes?: {
|
|
129
|
+
code: string;
|
|
130
|
+
tokens?: string[] | undefined;
|
|
131
|
+
}[] | undefined;
|
|
132
|
+
blocks?: {
|
|
133
|
+
name: string;
|
|
134
|
+
labelKey: string;
|
|
135
|
+
category: string;
|
|
136
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
137
|
+
fields: Record<string, {
|
|
138
|
+
type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
|
|
139
|
+
label?: string | undefined;
|
|
140
|
+
required?: boolean | undefined;
|
|
141
|
+
options?: {
|
|
142
|
+
label: string;
|
|
143
|
+
value: string | number;
|
|
144
|
+
}[] | undefined;
|
|
145
|
+
refKind?: string | undefined;
|
|
146
|
+
}>;
|
|
147
|
+
descriptionKey?: string | undefined;
|
|
148
|
+
defaultProps?: Record<string, unknown> | undefined;
|
|
149
|
+
responsiveFields?: string[] | undefined;
|
|
150
|
+
previewIcon?: string | undefined;
|
|
151
|
+
weight?: number | undefined;
|
|
152
|
+
}[] | undefined;
|
|
153
|
+
blockCategories?: {
|
|
154
|
+
key: string;
|
|
155
|
+
titleKey: string;
|
|
156
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
157
|
+
weight?: number | undefined;
|
|
158
|
+
visible?: boolean | undefined;
|
|
159
|
+
}[] | undefined;
|
|
160
|
+
env?: {
|
|
161
|
+
name: string;
|
|
162
|
+
describes: {
|
|
163
|
+
en: string;
|
|
164
|
+
pl: string;
|
|
165
|
+
};
|
|
166
|
+
requirement: {
|
|
167
|
+
kind: "required";
|
|
168
|
+
} | {
|
|
169
|
+
kind: "requiredWhen";
|
|
170
|
+
input: string;
|
|
171
|
+
equals: string;
|
|
172
|
+
} | {
|
|
173
|
+
kind: "optional";
|
|
174
|
+
without: {
|
|
175
|
+
en: string;
|
|
176
|
+
pl: string;
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
secret: boolean;
|
|
180
|
+
generable: boolean;
|
|
181
|
+
owner: {
|
|
182
|
+
kind: "platform";
|
|
183
|
+
} | {
|
|
184
|
+
kind: "application";
|
|
185
|
+
application: "admin" | "backend" | "storefront";
|
|
186
|
+
} | {
|
|
187
|
+
kind: "module";
|
|
188
|
+
moduleId: string;
|
|
189
|
+
};
|
|
190
|
+
consumers: ("admin" | "backend" | "storefront")[];
|
|
191
|
+
addressOf: "admin" | "backend" | "storefront" | null;
|
|
192
|
+
}[] | undefined;
|
|
193
|
+
};
|
|
194
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;CAgDlC,CAAC;AAEH,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,yBAAyB,mBAAmB,CAAC;AAE1D,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CnB,CAAC"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { defineModuleManifest, defineModuleSettingsManifest } from '@endora-commerce/contracts';
|
|
2
|
+
import { META_ADS_SETTING_CODES } from '@endora-commerce/contracts';
|
|
3
|
+
/**
|
|
4
|
+
* Meta Ads module (feature 064). Puts the Meta Pixel on the storefront per sales
|
|
5
|
+
* channel, reports Meta's standard commerce events, and lets an operator add
|
|
6
|
+
* custom events alongside them.
|
|
7
|
+
*
|
|
8
|
+
* Per-channel configuration is stored through the Settings module. Server-side
|
|
9
|
+
* reporting through Meta's Conversions API is deliberately out of v1 — see
|
|
10
|
+
* specs/064-meta-ads/research.md §R7.
|
|
11
|
+
*/
|
|
12
|
+
export const metaAdsSettingsManifest = defineModuleSettingsManifest({
|
|
13
|
+
moduleCode: 'meta_ads',
|
|
14
|
+
groups: [{ code: 'meta_ads', name: 'Meta Ads' }],
|
|
15
|
+
settings: [
|
|
16
|
+
{
|
|
17
|
+
// Feature 073 — the operator's activation control. **Platform-wide, and
|
|
18
|
+
// deliberately NOT `meta_ads.enabled`.** That code already exists and
|
|
19
|
+
// means something else: it is per-sales-channel and answers "is the
|
|
20
|
+
// pixel is live on a given storefront channel". This one answers "does this client have the Meta Ads
|
|
21
|
+
// capability at all". Adopting the existing code would have collapsed two
|
|
22
|
+
// orthogonal questions into one switch — and, because an activation
|
|
23
|
+
// control may only be written through the activation endpoint, would have
|
|
24
|
+
// made the per-channel setting unwritable through the settings screen.
|
|
25
|
+
code: 'meta_ads.module_enabled',
|
|
26
|
+
name: 'Meta Ads module enabled',
|
|
27
|
+
description: 'Switches Meta Pixel injection, the custom-event mappings and the admin screen on or off for the whole platform. Separate from the per-channel switch, which decides where the tag actually loads. Nothing is dropped: mappings stay in the database and every setting keeps its value.',
|
|
28
|
+
groupCode: 'meta_ads',
|
|
29
|
+
valueType: 'boolean',
|
|
30
|
+
defaultValue: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
code: META_ADS_SETTING_CODES.ENABLED,
|
|
34
|
+
name: 'Enable Meta Ads',
|
|
35
|
+
description: 'Master switch for the module. Per-channel overridable.',
|
|
36
|
+
groupCode: 'meta_ads',
|
|
37
|
+
valueType: 'boolean',
|
|
38
|
+
defaultValue: false,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
code: META_ADS_SETTING_CODES.PIXEL_ID,
|
|
42
|
+
name: 'Pixel ID',
|
|
43
|
+
description: 'Meta Pixel ID from Events Manager. Blank means the channel is untracked.',
|
|
44
|
+
groupCode: 'meta_ads',
|
|
45
|
+
valueType: 'string',
|
|
46
|
+
defaultValue: '',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
code: META_ADS_SETTING_CODES.REQUIRE_CONSENT,
|
|
50
|
+
name: 'Require analytics consent',
|
|
51
|
+
description: 'When enabled, nothing Meta-related runs until the visitor accepts the cookie banner.',
|
|
52
|
+
groupCode: 'meta_ads',
|
|
53
|
+
valueType: 'boolean',
|
|
54
|
+
defaultValue: true,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
export const META_ADS_READ_PERMISSION = 'meta_ads:read';
|
|
59
|
+
export const META_ADS_WRITE_PERMISSION = 'meta_ads:write';
|
|
60
|
+
export const manifest = defineModuleManifest({
|
|
61
|
+
id: 'meta_ads',
|
|
62
|
+
name: 'Meta Ads',
|
|
63
|
+
description: 'Meta Ads integration: per-sales-channel Meta Pixel, consent-gated tracking, standard commerce events, and configurable custom events.',
|
|
64
|
+
version: '1.0.0',
|
|
65
|
+
// `auth` owns the `requireAdmin` port the admin routes are gated by; feature
|
|
66
|
+
// 072 made it a container resolution rather than a constructor argument.
|
|
67
|
+
dependencies: ['audit_logs', 'sales_channels', 'settings', 'auth'],
|
|
68
|
+
settings: metaAdsSettingsManifest,
|
|
69
|
+
i18n: { bundlesDir: 'i18n' },
|
|
70
|
+
docs: { dir: 'docs' },
|
|
71
|
+
permissions: [
|
|
72
|
+
{ code: META_ADS_READ_PERMISSION, label: 'View Meta Ads configuration' },
|
|
73
|
+
{
|
|
74
|
+
code: META_ADS_WRITE_PERMISSION,
|
|
75
|
+
label: 'Manage Meta Ads configuration and custom events',
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
actions: [
|
|
79
|
+
{
|
|
80
|
+
id: 'open-meta-ads',
|
|
81
|
+
labelKey: 'actions.openMetaAds.label',
|
|
82
|
+
descriptionKey: 'actions.openMetaAds.description',
|
|
83
|
+
icon: 'Sparkles',
|
|
84
|
+
targetRoute: '/meta-ads',
|
|
85
|
+
requiredPermission: META_ADS_READ_PERMISSION,
|
|
86
|
+
keywords: ['meta', 'facebook', 'instagram', 'pixel', 'ads', 'reklamy'],
|
|
87
|
+
weight: 244,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'new-meta-custom-event',
|
|
91
|
+
labelKey: 'actions.newCustomEvent.label',
|
|
92
|
+
descriptionKey: 'actions.newCustomEvent.description',
|
|
93
|
+
icon: 'Plus',
|
|
94
|
+
targetRoute: '/meta-ads/new',
|
|
95
|
+
requiredPermission: META_ADS_WRITE_PERMISSION,
|
|
96
|
+
keywords: ['meta', 'pixel', 'event', 'zdarzenie', 'custom'],
|
|
97
|
+
weight: 245,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
activation: { settingCode: 'meta_ads.module_enabled', default: true },
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Feature 064 — Meta Ads. Creates `meta_custom_event_mappings`.
|
|
4
|
+
*
|
|
5
|
+
* Channel configuration (enabled, pixel id, consent) lives in the Settings
|
|
6
|
+
* module and needs no schema here.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Migration20260728T002715MetaAdsInit extends Migration {
|
|
9
|
+
up(): Promise<void>;
|
|
10
|
+
down(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=20260728T002715_meta_ads_init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260728T002715_meta_ads_init.d.ts","sourceRoot":"","sources":["../../src/migrations/20260728T002715_meta_ads_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;GAKG;AACH,qBAAa,mCAAoC,SAAQ,SAAS;IACjD,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Feature 064 — Meta Ads. Creates `meta_custom_event_mappings`.
|
|
4
|
+
*
|
|
5
|
+
* Channel configuration (enabled, pixel id, consent) lives in the Settings
|
|
6
|
+
* module and needs no schema here.
|
|
7
|
+
*/
|
|
8
|
+
export class Migration20260728T002715MetaAdsInit extends Migration {
|
|
9
|
+
async up() {
|
|
10
|
+
this.addSql(`
|
|
11
|
+
create table "meta_custom_event_mappings" (
|
|
12
|
+
"id" uuid not null,
|
|
13
|
+
"sales_channel_id" uuid null,
|
|
14
|
+
"trigger_action" varchar(32) not null,
|
|
15
|
+
"event_name" varchar(64) not null,
|
|
16
|
+
"enabled" boolean not null default true,
|
|
17
|
+
"version" int not null default 1,
|
|
18
|
+
"created_at" timestamptz not null,
|
|
19
|
+
"updated_at" timestamptz not null,
|
|
20
|
+
constraint "meta_custom_event_mappings_pkey" primary key ("id")
|
|
21
|
+
);
|
|
22
|
+
`);
|
|
23
|
+
this.addSql(`
|
|
24
|
+
alter table "meta_custom_event_mappings"
|
|
25
|
+
add constraint "meta_custom_event_mappings_sales_channel_id_foreign"
|
|
26
|
+
foreign key ("sales_channel_id") references "sales_channels" ("id")
|
|
27
|
+
on update cascade on delete cascade;
|
|
28
|
+
`);
|
|
29
|
+
this.addSql('create index "meta_custom_event_mappings_channel_action_idx" on "meta_custom_event_mappings" ("sales_channel_id", "trigger_action");');
|
|
30
|
+
}
|
|
31
|
+
async down() {
|
|
32
|
+
this.addSql('drop table if exists "meta_custom_event_mappings" cascade;');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=20260728T002715_meta_ads_init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260728T002715_meta_ads_init.js","sourceRoot":"","sources":["../../src/migrations/20260728T002715_meta_ads_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,OAAO,mCAAoC,SAAQ,SAAS;IACvD,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;KAYX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC;;;;;KAKX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,sIAAsI,CACvI,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,4DAA4D,CAAC,CAAC;IAC5E,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `./migrations` subpath — every migration class this module owns, as one
|
|
3
|
+
* ordered `migrations` array.
|
|
4
|
+
*
|
|
5
|
+
* The array is what the platform reads when this module is **installed**:
|
|
6
|
+
* `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
|
|
7
|
+
* the package outright when it is absent (D-168).
|
|
8
|
+
*
|
|
9
|
+
* Listed in ascending timestamp, which is the order of this module's own
|
|
10
|
+
* migrations and of nothing else (feature 081): a manifest `dependencies` array
|
|
11
|
+
* is the only thing ordering this block against another module's.
|
|
12
|
+
*
|
|
13
|
+
* The **named** exports stay beside the array, and the asymmetry with
|
|
14
|
+
* `./backend` — which publishes an array and no named class (D-168) — is
|
|
15
|
+
* deliberate. `db/migrations-registry.generated.ts` imports each class by name
|
|
16
|
+
* from this specifier, and a migration class name is contract in a way an entity
|
|
17
|
+
* class name is not: `mikro_orm_migrations` persists it, so it is a string every
|
|
18
|
+
* already-migrated database holds.
|
|
19
|
+
*
|
|
20
|
+
* A class that is in neither the array nor the barrel is a migration that does
|
|
21
|
+
* not run: `migration:pending` reports nothing pending and the first symptom is
|
|
22
|
+
* a query against a table nobody created.
|
|
23
|
+
*/
|
|
24
|
+
import { Migration20260728T002715MetaAdsInit } from './20260728T002715_meta_ads_init.js';
|
|
25
|
+
export declare const migrations: (typeof Migration20260728T002715MetaAdsInit)[];
|
|
26
|
+
export { Migration20260728T002715MetaAdsInit, };
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,mCAAmC,EAAE,MAAM,oCAAoC,CAAC;AAEzF,eAAO,MAAM,UAAU,gDAEtB,CAAC;AAEF,OAAO,EACL,mCAAmC,GACpC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `./migrations` subpath — every migration class this module owns, as one
|
|
3
|
+
* ordered `migrations` array.
|
|
4
|
+
*
|
|
5
|
+
* The array is what the platform reads when this module is **installed**:
|
|
6
|
+
* `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
|
|
7
|
+
* the package outright when it is absent (D-168).
|
|
8
|
+
*
|
|
9
|
+
* Listed in ascending timestamp, which is the order of this module's own
|
|
10
|
+
* migrations and of nothing else (feature 081): a manifest `dependencies` array
|
|
11
|
+
* is the only thing ordering this block against another module's.
|
|
12
|
+
*
|
|
13
|
+
* The **named** exports stay beside the array, and the asymmetry with
|
|
14
|
+
* `./backend` — which publishes an array and no named class (D-168) — is
|
|
15
|
+
* deliberate. `db/migrations-registry.generated.ts` imports each class by name
|
|
16
|
+
* from this specifier, and a migration class name is contract in a way an entity
|
|
17
|
+
* class name is not: `mikro_orm_migrations` persists it, so it is a string every
|
|
18
|
+
* already-migrated database holds.
|
|
19
|
+
*
|
|
20
|
+
* A class that is in neither the array nor the barrel is a migration that does
|
|
21
|
+
* not run: `migration:pending` reports nothing pending and the first symptom is
|
|
22
|
+
* a query against a table nobody created.
|
|
23
|
+
*/
|
|
24
|
+
import { Migration20260728T002715MetaAdsInit } from './20260728T002715_meta_ads_init.js';
|
|
25
|
+
export const migrations = [
|
|
26
|
+
Migration20260728T002715MetaAdsInit,
|
|
27
|
+
];
|
|
28
|
+
export { Migration20260728T002715MetaAdsInit, };
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,mCAAmC,EAAE,MAAM,oCAAoC,CAAC;AAEzF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,mCAAmC;CACpC,CAAC;AAEF,OAAO,EACL,mCAAmC,GACpC,CAAC"}
|
package/docs/meta-ads.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Meta Ads
|
|
3
|
+
description: Meta Pixel per sales channel, with the standard commerce events and optional custom events on top
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Meta Ads
|
|
7
|
+
|
|
8
|
+
Puts the **Meta Pixel** on the storefront per sales channel and reports Meta's standard commerce
|
|
9
|
+
events, with optional custom events on top.
|
|
10
|
+
|
|
11
|
+
Module id `meta_ads`.
|
|
12
|
+
|
|
13
|
+
## Configuration (Settings module)
|
|
14
|
+
|
|
15
|
+
All values live in the **Meta Ads** settings group and are overridable per sales channel.
|
|
16
|
+
|
|
17
|
+
| Setting | Type | Default | Meaning |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| `meta_ads.enabled` | boolean | `false` | Master switch for the channel. |
|
|
20
|
+
| `meta_ads.pixel_id` | string | `''` | Pixel ID from Events Manager. |
|
|
21
|
+
| `meta_ads.require_consent` | boolean | `true` | Gate all Meta behaviour on the cookie-banner decision. |
|
|
22
|
+
|
|
23
|
+
A channel is tracked only when the master switch is on **and** the Pixel ID is non-blank. A blank
|
|
24
|
+
Pixel ID is "not configured", never an error the visitor sees, so clearing it is a safe way to pause
|
|
25
|
+
a channel.
|
|
26
|
+
|
|
27
|
+
## Storefront behaviour
|
|
28
|
+
|
|
29
|
+
The Pixel loads asynchronously and never blocks the first paint. Nothing Meta-related runs — no
|
|
30
|
+
script, no cookie, no request — until the visitor accepts the cookie banner, unless the operator has
|
|
31
|
+
turned the consent requirement off. A visitor who accepts mid-session is tracked from that moment
|
|
32
|
+
without a reload.
|
|
33
|
+
|
|
34
|
+
Consent is the storefront's single decision, shared with Google Analytics and LinkedIn Ads, so there
|
|
35
|
+
is only ever one banner and one stored answer. Each platform gates independently: turning Meta on or
|
|
36
|
+
off does not affect the others.
|
|
37
|
+
|
|
38
|
+
## Standard events
|
|
39
|
+
|
|
40
|
+
These fire automatically for the storefront actions the platform already emits, with the parameters
|
|
41
|
+
Meta expects for catalogue matching and ROAS reporting:
|
|
42
|
+
|
|
43
|
+
| Storefront action | Meta event |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| Product viewed | `ViewContent` |
|
|
46
|
+
| Added to cart | `AddToCart` |
|
|
47
|
+
| Checkout started | `InitiateCheckout` |
|
|
48
|
+
| Order completed | `Purchase` |
|
|
49
|
+
| Added to quote request | `Lead` |
|
|
50
|
+
| Contact form submitted | `Lead` |
|
|
51
|
+
| Added to shopping list | *(none)* |
|
|
52
|
+
| Place Order clicked | *(none)* |
|
|
53
|
+
|
|
54
|
+
Names are fixed in code, not editable — Meta's optimisation and catalogue matching key off these
|
|
55
|
+
exact names, so renaming them would quietly downgrade your reporting.
|
|
56
|
+
|
|
57
|
+
## Custom events
|
|
58
|
+
|
|
59
|
+
Under **Admin → Meta Ads** you can report any of those storefront actions under your own event name
|
|
60
|
+
too — for example, "added to quote request" as `SubmitQuote` for a B2B-specific audience.
|
|
61
|
+
|
|
62
|
+
A custom event is **additive**: the standard event still fires, and yours fires beside it. It never
|
|
63
|
+
replaces the standard event, so adding an audience event cannot cost you your `Purchase` reporting.
|
|
64
|
+
The two actions with no standard event accept custom mappings, which is exactly what they are for.
|
|
65
|
+
|
|
66
|
+
A mapping can target one sales channel or all of them, and can be disabled without deleting it.
|
|
67
|
+
|
|
68
|
+
## Permissions
|
|
69
|
+
|
|
70
|
+
| Code | Grants |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `meta_ads:read` | View custom events and the module page. |
|
|
73
|
+
| `meta_ads:write` | Create, edit, and delete custom events. |
|
|
74
|
+
|
|
75
|
+
Both appear on `/admin-roles`. Changes are audited with the acting operator.
|
|
76
|
+
|
|
77
|
+
## Not yet implemented
|
|
78
|
+
|
|
79
|
+
- **Conversions API (server-side reporting).** Browser pixels are heavily ad-blocked, so expect
|
|
80
|
+
reported conversions to under-count until this lands. It needs an access token and an event ID
|
|
81
|
+
shared with the browser event for deduplication, and is worth doing once for both ad platforms.
|
|
82
|
+
- **Conversion retraction** for refunded or cancelled orders.
|
package/i18n/en.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"actions.openMetaAds.label": "Meta Ads",
|
|
3
|
+
"actions.openMetaAds.description": "Configure Meta Pixel custom events",
|
|
4
|
+
"actions.newCustomEvent.label": "New Meta custom event",
|
|
5
|
+
"actions.newCustomEvent.description": "Report a storefront action as a custom Meta event",
|
|
6
|
+
"nav.metaAds.label": "Meta Ads",
|
|
7
|
+
"customEvents.title": "Custom events",
|
|
8
|
+
"customEvents.subtitle": "Report a storefront action under your own Meta event name. Standard events (ViewContent, AddToCart, Purchase…) are always reported and are not affected by these.",
|
|
9
|
+
"customEvents.empty": "No custom events yet",
|
|
10
|
+
"customEvents.new": "New custom event",
|
|
11
|
+
"customEvents.edit": "Edit custom event",
|
|
12
|
+
"customEvents.triggerAction": "Storefront action",
|
|
13
|
+
"customEvents.eventName": "Meta event name",
|
|
14
|
+
"customEvents.eventNameHint": "Letters, digits and underscores, e.g. SubmitQuote.",
|
|
15
|
+
"customEvents.standardEvent": "Standard event",
|
|
16
|
+
"customEvents.standardEventNone": "none",
|
|
17
|
+
"customEvents.salesChannel": "Sales channel",
|
|
18
|
+
"customEvents.allChannels": "All channels",
|
|
19
|
+
"customEvents.enabled": "Enabled",
|
|
20
|
+
"customEvents.save": "Save",
|
|
21
|
+
"customEvents.delete": "Delete",
|
|
22
|
+
"customEvents.deleteConfirm": "Delete this custom event?",
|
|
23
|
+
"customEvents.action.product_viewed": "Product viewed",
|
|
24
|
+
"customEvents.action.add_to_cart": "Added to cart",
|
|
25
|
+
"customEvents.action.add_to_quote_request": "Added to quote request",
|
|
26
|
+
"customEvents.action.add_to_shopping_list": "Added to shopping list",
|
|
27
|
+
"customEvents.action.begin_checkout": "Checkout started",
|
|
28
|
+
"customEvents.action.place_order_clicked": "Place Order clicked",
|
|
29
|
+
"customEvents.action.purchase": "Order completed",
|
|
30
|
+
"customEvents.action.contact_form_submitted": "Contact form submitted",
|
|
31
|
+
"error.generic": "Something went wrong.",
|
|
32
|
+
"error.notFound": "Custom event not found.",
|
|
33
|
+
"error.versionConflict": "Someone else changed this custom event. Reload and reapply your edit.",
|
|
34
|
+
"error.validationFailed": "Some fields are invalid."
|
|
35
|
+
}
|
package/i18n/pl.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"actions.openMetaAds.label": "Meta Ads",
|
|
3
|
+
"actions.openMetaAds.description": "Konfiguruj własne zdarzenia Meta Pixel",
|
|
4
|
+
"actions.newCustomEvent.label": "Nowe zdarzenie własne Meta",
|
|
5
|
+
"actions.newCustomEvent.description": "Raportuj akcję w Storefront jako własne zdarzenie Meta",
|
|
6
|
+
"nav.metaAds.label": "Meta Ads",
|
|
7
|
+
"customEvents.title": "Zdarzenia własne",
|
|
8
|
+
"customEvents.subtitle": "Raportuj akcję w Storefront pod własną nazwą zdarzenia Meta. Zdarzenia standardowe (ViewContent, AddToCart, Purchase…) są wysyłane zawsze i nie zależą od tych ustawień.",
|
|
9
|
+
"customEvents.empty": "Brak zdarzeń własnych",
|
|
10
|
+
"customEvents.new": "Nowe zdarzenie własne",
|
|
11
|
+
"customEvents.edit": "Edytuj zdarzenie własne",
|
|
12
|
+
"customEvents.triggerAction": "Akcja w Storefront",
|
|
13
|
+
"customEvents.eventName": "Nazwa zdarzenia Meta",
|
|
14
|
+
"customEvents.eventNameHint": "Litery, cyfry i podkreślenia, np. SubmitQuote.",
|
|
15
|
+
"customEvents.standardEvent": "Zdarzenie standardowe",
|
|
16
|
+
"customEvents.standardEventNone": "brak",
|
|
17
|
+
"customEvents.salesChannel": "Kanał sprzedaży",
|
|
18
|
+
"customEvents.allChannels": "Wszystkie kanały",
|
|
19
|
+
"customEvents.enabled": "Aktywne",
|
|
20
|
+
"customEvents.save": "Zapisz",
|
|
21
|
+
"customEvents.delete": "Usuń",
|
|
22
|
+
"customEvents.deleteConfirm": "Usunąć to zdarzenie własne?",
|
|
23
|
+
"customEvents.action.product_viewed": "Wyświetlenie produktu",
|
|
24
|
+
"customEvents.action.add_to_cart": "Dodanie do koszyka",
|
|
25
|
+
"customEvents.action.add_to_quote_request": "Dodanie do zapytania ofertowego",
|
|
26
|
+
"customEvents.action.add_to_shopping_list": "Dodanie do listy zakupowej",
|
|
27
|
+
"customEvents.action.begin_checkout": "Rozpoczęcie zamówienia",
|
|
28
|
+
"customEvents.action.place_order_clicked": "Kliknięcie „Złóż zamówienie”",
|
|
29
|
+
"customEvents.action.purchase": "Złożenie zamówienia",
|
|
30
|
+
"customEvents.action.contact_form_submitted": "Zapis formularza kontaktowego",
|
|
31
|
+
"error.generic": "Coś poszło nie tak.",
|
|
32
|
+
"error.notFound": "Nie znaleziono zdarzenia własnego.",
|
|
33
|
+
"error.versionConflict": "Ktoś inny zmienił to zdarzenie. Odśwież stronę i ponów zmianę.",
|
|
34
|
+
"error.validationFailed": "Niektóre pola są nieprawidłowe."
|
|
35
|
+
}
|