@progus/connector 0.2.0 → 0.4.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/README.md +13 -5
- package/dist/index.cjs +213 -34
- package/dist/index.d.cts +46 -2
- package/dist/index.d.ts +46 -2
- package/dist/index.js +209 -33
- package/package.json +15 -7
- package/src/appsCatalog.json +0 -26
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @progus/connector
|
|
2
2
|
|
|
3
3
|
Headless connector for Progus partner/affiliate tracking, partner ID assignment,
|
|
4
|
-
and cross-sell offers.
|
|
4
|
+
and cross-sell offers.
|
|
5
5
|
|
|
6
6
|
## Installation
|
|
7
7
|
|
|
@@ -18,7 +18,7 @@ const connector = createProgusConnector({
|
|
|
18
18
|
appKey: "progus-store-locator",
|
|
19
19
|
apiBaseUrl: process.env.PARTNERS_API_URL!,
|
|
20
20
|
signingSecret: process.env.PARTNERS_SECRET_KEY!,
|
|
21
|
-
apiKey: process.env.PARTNERS_API_KEY,
|
|
21
|
+
apiKey: process.env.PARTNERS_API_KEY,
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
await connector.trackInstall({
|
|
@@ -51,14 +51,16 @@ if (result.success) {
|
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
## Cross-sell offers
|
|
54
|
+
## Cross-sell offers (remote catalog)
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
|
-
import {
|
|
57
|
+
import { getCrossSellOffersFromApi } from "@progus/connector";
|
|
58
58
|
|
|
59
|
-
const offers =
|
|
59
|
+
const offers = await getCrossSellOffersFromApi({
|
|
60
60
|
currentAppKey: "progus-store-locator",
|
|
61
61
|
installedAppKeys: ["progus_cod"],
|
|
62
|
+
appsCatalogUrl: "https://appsdata.progus.workers.dev/recommendations",
|
|
63
|
+
limit: 3,
|
|
62
64
|
});
|
|
63
65
|
```
|
|
64
66
|
|
|
@@ -70,6 +72,12 @@ import { signPayload } from "@progus/connector";
|
|
|
70
72
|
const signature = signPayload(JSON.stringify({ test: true }), process.env.PARTNERS_SECRET_KEY!);
|
|
71
73
|
```
|
|
72
74
|
|
|
75
|
+
## UI components
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { Recommendations, PartnerIdCard } from "@progus/connector";
|
|
79
|
+
```
|
|
80
|
+
|
|
73
81
|
## Environment variables
|
|
74
82
|
|
|
75
83
|
- `PARTNERS_API_URL` - base URL for partner API (e.g. `https://partners.example.com`)
|
package/dist/index.cjs
CHANGED
|
@@ -20,9 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
|
|
23
|
+
PartnerIdCard: () => PartnerIdCard,
|
|
24
|
+
Recommendations: () => Recommendations,
|
|
24
25
|
createProgusConnector: () => createProgusConnector,
|
|
26
|
+
fetchAppsCatalog: () => fetchAppsCatalog,
|
|
25
27
|
getCrossSellOffers: () => getCrossSellOffers,
|
|
28
|
+
getCrossSellOffersFromApi: () => getCrossSellOffersFromApi,
|
|
26
29
|
normalizePartnerId: () => normalizePartnerId,
|
|
27
30
|
signPayload: () => signPayload
|
|
28
31
|
});
|
|
@@ -209,52 +212,228 @@ function createProgusConnector(config) {
|
|
|
209
212
|
};
|
|
210
213
|
}
|
|
211
214
|
|
|
212
|
-
// src/appsCatalog.json
|
|
213
|
-
var appsCatalog_default = [
|
|
214
|
-
{
|
|
215
|
-
key: "progus_cod",
|
|
216
|
-
type: "app",
|
|
217
|
-
title: "Progus COD",
|
|
218
|
-
company: "Progus",
|
|
219
|
-
companyUrl: "https://progus.com",
|
|
220
|
-
desc: "Automate COD Fees & Hide/Show Cash on Delivery by Rules",
|
|
221
|
-
url: "https://apps.shopify.com/progus-cod",
|
|
222
|
-
icon: "https://cdn.shopify.com/app-store/listing_images/bc537219cc3ed2bd4e7e3e683fe6b74a/icon/CMi_6dTEkIoDEAE=.png",
|
|
223
|
-
priority: 100,
|
|
224
|
-
enabled: true
|
|
225
|
-
},
|
|
226
|
-
{
|
|
227
|
-
key: "progus_trust_badges",
|
|
228
|
-
type: "app",
|
|
229
|
-
title: "Progus Trust Badges",
|
|
230
|
-
company: "Progus",
|
|
231
|
-
companyUrl: "https://progus.com",
|
|
232
|
-
desc: "Add Trust Badges to your store to build trust and credibility.",
|
|
233
|
-
url: "https://apps.shopify.com/progus-trust-badges-1",
|
|
234
|
-
icon: "https://cdn.shopify.com/app-store/listing_images/f9d0009e237f27d2db35b41ef99be858/icon/CJ3y1qDn1JEDEAE=.png",
|
|
235
|
-
priority: 90,
|
|
236
|
-
enabled: true
|
|
237
|
-
}
|
|
238
|
-
];
|
|
239
|
-
|
|
240
215
|
// src/crossSell.ts
|
|
241
|
-
var catalog = appsCatalog_default;
|
|
242
216
|
function getCrossSellOffers(options = {}) {
|
|
243
|
-
const
|
|
217
|
+
const appsCatalog = options.appsCatalog ?? [];
|
|
244
218
|
const installedKeys = new Set(
|
|
245
219
|
[options.currentAppKey, ...options.installedAppKeys ?? []].filter(
|
|
246
220
|
(key) => Boolean(key)
|
|
247
221
|
)
|
|
248
222
|
);
|
|
249
223
|
const locale = options.locale;
|
|
250
|
-
return
|
|
224
|
+
return appsCatalog.filter((app) => app.enabled !== false).filter((app) => locale ? !app.locales || app.locales.includes(locale) : true).filter((app) => !installedKeys.has(app.key)).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
225
|
+
}
|
|
226
|
+
var DEFAULT_APPS_CATALOG_URL = "https://appsdata.progus.workers.dev/recommendations";
|
|
227
|
+
async function fetchAppsCatalog(options = {}) {
|
|
228
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
229
|
+
const logger = options.logger ?? console;
|
|
230
|
+
const appName = options.appName ?? options.currentAppKey;
|
|
231
|
+
const baseUrl = stripTrailingSlash(options.appsCatalogUrl ?? DEFAULT_APPS_CATALOG_URL);
|
|
232
|
+
const limit = typeof options.limit === "number" ? options.limit : 3;
|
|
233
|
+
const params = new URLSearchParams();
|
|
234
|
+
if (appName) params.set("appName", appName);
|
|
235
|
+
if (limit > 0) params.set("limit", String(limit));
|
|
236
|
+
const url = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
|
|
237
|
+
if (!fetchImpl) {
|
|
238
|
+
throw new Error("Fetch implementation is required");
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const response = await fetchImpl(url);
|
|
242
|
+
const text = await response.text();
|
|
243
|
+
const parsed = safeJsonParse(text);
|
|
244
|
+
if (!response.ok || !parsed) {
|
|
245
|
+
logger?.error?.("Failed to fetch apps catalog", { status: response.status });
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
return parsed;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
logger?.error?.("Failed to fetch apps catalog", {
|
|
251
|
+
error: error instanceof Error ? error.message : String(error)
|
|
252
|
+
});
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async function getCrossSellOffersFromApi(options = {}) {
|
|
257
|
+
const appsCatalog = options.appsCatalog ?? await fetchAppsCatalog(options);
|
|
258
|
+
return getCrossSellOffers({ ...options, appsCatalog });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// src/ui/PartnerIdCard.tsx
|
|
262
|
+
var import_polaris = require("@shopify/polaris");
|
|
263
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
264
|
+
function PartnerIdCard({
|
|
265
|
+
partnerId,
|
|
266
|
+
onPartnerIdChange,
|
|
267
|
+
onSave,
|
|
268
|
+
saveLabel,
|
|
269
|
+
loading,
|
|
270
|
+
saving,
|
|
271
|
+
locked,
|
|
272
|
+
error,
|
|
273
|
+
label,
|
|
274
|
+
placeholder,
|
|
275
|
+
helpText,
|
|
276
|
+
saveDisabled
|
|
277
|
+
}) {
|
|
278
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
279
|
+
import_polaris.LegacyCard,
|
|
280
|
+
{
|
|
281
|
+
sectioned: true,
|
|
282
|
+
primaryFooterAction: {
|
|
283
|
+
content: saveLabel,
|
|
284
|
+
loading: Boolean(saving),
|
|
285
|
+
onAction: onSave,
|
|
286
|
+
disabled: Boolean(saveDisabled)
|
|
287
|
+
},
|
|
288
|
+
children: loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_polaris.SkeletonBodyText, { lines: 1 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_polaris.FormLayout, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
289
|
+
import_polaris.TextField,
|
|
290
|
+
{
|
|
291
|
+
error,
|
|
292
|
+
label,
|
|
293
|
+
value: partnerId,
|
|
294
|
+
onChange: onPartnerIdChange,
|
|
295
|
+
placeholder,
|
|
296
|
+
helpText,
|
|
297
|
+
autoComplete: "off",
|
|
298
|
+
disabled: Boolean(locked || loading)
|
|
299
|
+
}
|
|
300
|
+
) })
|
|
301
|
+
}
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/ui/Recommendations.tsx
|
|
306
|
+
var import_polaris2 = require("@shopify/polaris");
|
|
307
|
+
var import_polaris_icons = require("@shopify/polaris-icons");
|
|
308
|
+
var import_react = require("react");
|
|
309
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
310
|
+
function Recommendations({
|
|
311
|
+
recommendations,
|
|
312
|
+
loading,
|
|
313
|
+
title,
|
|
314
|
+
dismissLabel,
|
|
315
|
+
installLabel,
|
|
316
|
+
freePlanLabel,
|
|
317
|
+
newBadgeLabel,
|
|
318
|
+
onDismiss,
|
|
319
|
+
openUrl,
|
|
320
|
+
getDescription
|
|
321
|
+
}) {
|
|
322
|
+
const [menuActive, setMenuActive] = (0, import_react.useState)(false);
|
|
323
|
+
if (!recommendations || recommendations.length === 0) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
const handleOpenUrl = (url) => {
|
|
327
|
+
if (openUrl) return openUrl(url);
|
|
328
|
+
if (typeof window !== "undefined") {
|
|
329
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
333
|
+
import_polaris2.LegacyCard,
|
|
334
|
+
{
|
|
335
|
+
sectioned: true,
|
|
336
|
+
title: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
337
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Text, { variant: "headingSm", as: "h3", children: title }),
|
|
338
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
339
|
+
import_polaris2.Popover,
|
|
340
|
+
{
|
|
341
|
+
active: menuActive,
|
|
342
|
+
activator: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
343
|
+
import_polaris2.Button,
|
|
344
|
+
{
|
|
345
|
+
onClick: () => setMenuActive(!menuActive),
|
|
346
|
+
icon: import_polaris_icons.MenuHorizontalIcon,
|
|
347
|
+
variant: "plain",
|
|
348
|
+
accessibilityLabel: "Menu"
|
|
349
|
+
}
|
|
350
|
+
),
|
|
351
|
+
onClose: () => setMenuActive(false),
|
|
352
|
+
preferredAlignment: "right",
|
|
353
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
354
|
+
import_polaris2.ActionList,
|
|
355
|
+
{
|
|
356
|
+
actionRole: "menuitem",
|
|
357
|
+
items: [
|
|
358
|
+
{
|
|
359
|
+
content: dismissLabel,
|
|
360
|
+
onAction: () => {
|
|
361
|
+
setMenuActive(false);
|
|
362
|
+
onDismiss();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
) })
|
|
370
|
+
] }),
|
|
371
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Layout, { children: recommendations.map((rec, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Layout.Section, { variant: "oneThird", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.LegacyCard, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_polaris2.LegacyCard.Section, { children: [
|
|
372
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: "16px", position: "relative" }, children: [
|
|
373
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
374
|
+
"div",
|
|
375
|
+
{
|
|
376
|
+
style: {
|
|
377
|
+
flexShrink: 0,
|
|
378
|
+
width: "48px",
|
|
379
|
+
height: "48px",
|
|
380
|
+
borderRadius: "12px",
|
|
381
|
+
overflow: "hidden",
|
|
382
|
+
backgroundColor: "#f6f6f7",
|
|
383
|
+
display: "flex",
|
|
384
|
+
alignItems: "center",
|
|
385
|
+
justifyContent: "center"
|
|
386
|
+
},
|
|
387
|
+
children: rec.icon ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
388
|
+
"img",
|
|
389
|
+
{
|
|
390
|
+
src: rec.icon,
|
|
391
|
+
alt: rec.title,
|
|
392
|
+
style: { width: "48px", height: "48px", objectFit: "cover" }
|
|
393
|
+
}
|
|
394
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
395
|
+
"div",
|
|
396
|
+
{
|
|
397
|
+
style: {
|
|
398
|
+
width: "48px",
|
|
399
|
+
height: "48px",
|
|
400
|
+
backgroundColor: "#e1e1e1",
|
|
401
|
+
borderRadius: "12px"
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
),
|
|
407
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { position: "relative" }, children: [
|
|
408
|
+
rec.newBadge && newBadgeLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", right: "-8px", top: "-8px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Badge, { tone: "info", children: newBadgeLabel }) }),
|
|
409
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Text, { variant: "headingSm", as: "h3", truncate: true, children: rec.title }),
|
|
410
|
+
freePlanLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "6px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Badge, { tone: "info", children: freePlanLabel }) })
|
|
411
|
+
] }) })
|
|
412
|
+
] }),
|
|
413
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Text, { variant: "bodySm", tone: "subdued", as: "p", children: getDescription ? getDescription(rec) : rec.desc }) }),
|
|
414
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
415
|
+
import_polaris2.Button,
|
|
416
|
+
{
|
|
417
|
+
disabled: loading,
|
|
418
|
+
size: "micro",
|
|
419
|
+
variant: "plain",
|
|
420
|
+
onClick: () => handleOpenUrl(rec.url),
|
|
421
|
+
icon: import_polaris_icons.ExternalIcon,
|
|
422
|
+
children: installLabel
|
|
423
|
+
}
|
|
424
|
+
) })
|
|
425
|
+
] }) }) }, `${rec.key}-${index}`)) })
|
|
426
|
+
}
|
|
427
|
+
);
|
|
251
428
|
}
|
|
252
|
-
var appsCatalog = catalog;
|
|
253
429
|
// Annotate the CommonJS export names for ESM import in node:
|
|
254
430
|
0 && (module.exports = {
|
|
255
|
-
|
|
431
|
+
PartnerIdCard,
|
|
432
|
+
Recommendations,
|
|
256
433
|
createProgusConnector,
|
|
434
|
+
fetchAppsCatalog,
|
|
257
435
|
getCrossSellOffers,
|
|
436
|
+
getCrossSellOffersFromApi,
|
|
258
437
|
normalizePartnerId,
|
|
259
438
|
signPayload
|
|
260
439
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { TextFieldProps } from '@shopify/polaris';
|
|
3
|
+
|
|
1
4
|
type FetchLike = typeof fetch;
|
|
2
5
|
type Logger = {
|
|
3
6
|
info?: (message: string, meta?: Record<string, unknown>) => void;
|
|
@@ -24,6 +27,13 @@ type CrossSellOptions = {
|
|
|
24
27
|
shopPlan?: string;
|
|
25
28
|
appsCatalog?: AppsCatalogEntry[];
|
|
26
29
|
};
|
|
30
|
+
type CrossSellFetchOptions = CrossSellOptions & {
|
|
31
|
+
appName?: string;
|
|
32
|
+
appsCatalogUrl?: string;
|
|
33
|
+
limit?: number;
|
|
34
|
+
fetch?: FetchLike;
|
|
35
|
+
logger?: Logger;
|
|
36
|
+
};
|
|
27
37
|
type ConnectorConfig = {
|
|
28
38
|
appKey: string;
|
|
29
39
|
apiBaseUrl: string;
|
|
@@ -97,10 +107,44 @@ type Connector = {
|
|
|
97
107
|
declare function createProgusConnector(config: ConnectorConfig): Connector;
|
|
98
108
|
|
|
99
109
|
declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntry[];
|
|
100
|
-
declare
|
|
110
|
+
declare function fetchAppsCatalog(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
|
|
111
|
+
declare function getCrossSellOffersFromApi(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
|
|
101
112
|
|
|
102
113
|
declare function signPayload(body: string, secret: string): string;
|
|
103
114
|
|
|
104
115
|
declare function normalizePartnerId(value?: string | null): string | null;
|
|
105
116
|
|
|
106
|
-
|
|
117
|
+
type PartnerIdCardProps = {
|
|
118
|
+
partnerId: string;
|
|
119
|
+
onPartnerIdChange: (value: string) => void;
|
|
120
|
+
onSave: () => void;
|
|
121
|
+
saveLabel: string;
|
|
122
|
+
loading?: boolean;
|
|
123
|
+
saving?: boolean;
|
|
124
|
+
locked?: boolean;
|
|
125
|
+
error?: string | TextFieldProps["error"];
|
|
126
|
+
label: string;
|
|
127
|
+
placeholder?: string;
|
|
128
|
+
helpText?: string;
|
|
129
|
+
saveDisabled?: boolean;
|
|
130
|
+
};
|
|
131
|
+
declare function PartnerIdCard({ partnerId, onPartnerIdChange, onSave, saveLabel, loading, saving, locked, error, label, placeholder, helpText, saveDisabled, }: PartnerIdCardProps): react_jsx_runtime.JSX.Element;
|
|
132
|
+
|
|
133
|
+
type RecommendationItem = AppsCatalogEntry & {
|
|
134
|
+
newBadge?: boolean;
|
|
135
|
+
};
|
|
136
|
+
type RecommendationsProps = {
|
|
137
|
+
recommendations: RecommendationItem[];
|
|
138
|
+
loading?: boolean;
|
|
139
|
+
title: string;
|
|
140
|
+
dismissLabel: string;
|
|
141
|
+
installLabel: string;
|
|
142
|
+
freePlanLabel?: string;
|
|
143
|
+
newBadgeLabel?: string;
|
|
144
|
+
onDismiss: () => void;
|
|
145
|
+
openUrl?: (url: string) => void;
|
|
146
|
+
getDescription?: (item: RecommendationItem) => string;
|
|
147
|
+
};
|
|
148
|
+
declare function Recommendations({ recommendations, loading, title, dismissLabel, installLabel, freePlanLabel, newBadgeLabel, onDismiss, openUrl, getDescription, }: RecommendationsProps): react_jsx_runtime.JSX.Element | null;
|
|
149
|
+
|
|
150
|
+
export { type AppsCatalogEntry, type AssignPartnerIdInput, type CheckPartnerIdResult, type ConnectorConfig, type CrossSellFetchOptions, type CrossSellOptions, type Logger, PartnerIdCard, type PartnerIdCardProps, type RecommendationItem, Recommendations, type RecommendationsProps, type SubscriptionEventData, type TrackEventName, type TrackEventParams, type TrackResult, createProgusConnector, fetchAppsCatalog, getCrossSellOffers, getCrossSellOffersFromApi, normalizePartnerId, signPayload };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { TextFieldProps } from '@shopify/polaris';
|
|
3
|
+
|
|
1
4
|
type FetchLike = typeof fetch;
|
|
2
5
|
type Logger = {
|
|
3
6
|
info?: (message: string, meta?: Record<string, unknown>) => void;
|
|
@@ -24,6 +27,13 @@ type CrossSellOptions = {
|
|
|
24
27
|
shopPlan?: string;
|
|
25
28
|
appsCatalog?: AppsCatalogEntry[];
|
|
26
29
|
};
|
|
30
|
+
type CrossSellFetchOptions = CrossSellOptions & {
|
|
31
|
+
appName?: string;
|
|
32
|
+
appsCatalogUrl?: string;
|
|
33
|
+
limit?: number;
|
|
34
|
+
fetch?: FetchLike;
|
|
35
|
+
logger?: Logger;
|
|
36
|
+
};
|
|
27
37
|
type ConnectorConfig = {
|
|
28
38
|
appKey: string;
|
|
29
39
|
apiBaseUrl: string;
|
|
@@ -97,10 +107,44 @@ type Connector = {
|
|
|
97
107
|
declare function createProgusConnector(config: ConnectorConfig): Connector;
|
|
98
108
|
|
|
99
109
|
declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntry[];
|
|
100
|
-
declare
|
|
110
|
+
declare function fetchAppsCatalog(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
|
|
111
|
+
declare function getCrossSellOffersFromApi(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
|
|
101
112
|
|
|
102
113
|
declare function signPayload(body: string, secret: string): string;
|
|
103
114
|
|
|
104
115
|
declare function normalizePartnerId(value?: string | null): string | null;
|
|
105
116
|
|
|
106
|
-
|
|
117
|
+
type PartnerIdCardProps = {
|
|
118
|
+
partnerId: string;
|
|
119
|
+
onPartnerIdChange: (value: string) => void;
|
|
120
|
+
onSave: () => void;
|
|
121
|
+
saveLabel: string;
|
|
122
|
+
loading?: boolean;
|
|
123
|
+
saving?: boolean;
|
|
124
|
+
locked?: boolean;
|
|
125
|
+
error?: string | TextFieldProps["error"];
|
|
126
|
+
label: string;
|
|
127
|
+
placeholder?: string;
|
|
128
|
+
helpText?: string;
|
|
129
|
+
saveDisabled?: boolean;
|
|
130
|
+
};
|
|
131
|
+
declare function PartnerIdCard({ partnerId, onPartnerIdChange, onSave, saveLabel, loading, saving, locked, error, label, placeholder, helpText, saveDisabled, }: PartnerIdCardProps): react_jsx_runtime.JSX.Element;
|
|
132
|
+
|
|
133
|
+
type RecommendationItem = AppsCatalogEntry & {
|
|
134
|
+
newBadge?: boolean;
|
|
135
|
+
};
|
|
136
|
+
type RecommendationsProps = {
|
|
137
|
+
recommendations: RecommendationItem[];
|
|
138
|
+
loading?: boolean;
|
|
139
|
+
title: string;
|
|
140
|
+
dismissLabel: string;
|
|
141
|
+
installLabel: string;
|
|
142
|
+
freePlanLabel?: string;
|
|
143
|
+
newBadgeLabel?: string;
|
|
144
|
+
onDismiss: () => void;
|
|
145
|
+
openUrl?: (url: string) => void;
|
|
146
|
+
getDescription?: (item: RecommendationItem) => string;
|
|
147
|
+
};
|
|
148
|
+
declare function Recommendations({ recommendations, loading, title, dismissLabel, installLabel, freePlanLabel, newBadgeLabel, onDismiss, openUrl, getDescription, }: RecommendationsProps): react_jsx_runtime.JSX.Element | null;
|
|
149
|
+
|
|
150
|
+
export { type AppsCatalogEntry, type AssignPartnerIdInput, type CheckPartnerIdResult, type ConnectorConfig, type CrossSellFetchOptions, type CrossSellOptions, type Logger, PartnerIdCard, type PartnerIdCardProps, type RecommendationItem, Recommendations, type RecommendationsProps, type SubscriptionEventData, type TrackEventName, type TrackEventParams, type TrackResult, createProgusConnector, fetchAppsCatalog, getCrossSellOffers, getCrossSellOffersFromApi, normalizePartnerId, signPayload };
|
package/dist/index.js
CHANGED
|
@@ -179,51 +179,227 @@ function createProgusConnector(config) {
|
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
// src/appsCatalog.json
|
|
183
|
-
var appsCatalog_default = [
|
|
184
|
-
{
|
|
185
|
-
key: "progus_cod",
|
|
186
|
-
type: "app",
|
|
187
|
-
title: "Progus COD",
|
|
188
|
-
company: "Progus",
|
|
189
|
-
companyUrl: "https://progus.com",
|
|
190
|
-
desc: "Automate COD Fees & Hide/Show Cash on Delivery by Rules",
|
|
191
|
-
url: "https://apps.shopify.com/progus-cod",
|
|
192
|
-
icon: "https://cdn.shopify.com/app-store/listing_images/bc537219cc3ed2bd4e7e3e683fe6b74a/icon/CMi_6dTEkIoDEAE=.png",
|
|
193
|
-
priority: 100,
|
|
194
|
-
enabled: true
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
key: "progus_trust_badges",
|
|
198
|
-
type: "app",
|
|
199
|
-
title: "Progus Trust Badges",
|
|
200
|
-
company: "Progus",
|
|
201
|
-
companyUrl: "https://progus.com",
|
|
202
|
-
desc: "Add Trust Badges to your store to build trust and credibility.",
|
|
203
|
-
url: "https://apps.shopify.com/progus-trust-badges-1",
|
|
204
|
-
icon: "https://cdn.shopify.com/app-store/listing_images/f9d0009e237f27d2db35b41ef99be858/icon/CJ3y1qDn1JEDEAE=.png",
|
|
205
|
-
priority: 90,
|
|
206
|
-
enabled: true
|
|
207
|
-
}
|
|
208
|
-
];
|
|
209
|
-
|
|
210
182
|
// src/crossSell.ts
|
|
211
|
-
var catalog = appsCatalog_default;
|
|
212
183
|
function getCrossSellOffers(options = {}) {
|
|
213
|
-
const
|
|
184
|
+
const appsCatalog = options.appsCatalog ?? [];
|
|
214
185
|
const installedKeys = new Set(
|
|
215
186
|
[options.currentAppKey, ...options.installedAppKeys ?? []].filter(
|
|
216
187
|
(key) => Boolean(key)
|
|
217
188
|
)
|
|
218
189
|
);
|
|
219
190
|
const locale = options.locale;
|
|
220
|
-
return
|
|
191
|
+
return appsCatalog.filter((app) => app.enabled !== false).filter((app) => locale ? !app.locales || app.locales.includes(locale) : true).filter((app) => !installedKeys.has(app.key)).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
192
|
+
}
|
|
193
|
+
var DEFAULT_APPS_CATALOG_URL = "https://appsdata.progus.workers.dev/recommendations";
|
|
194
|
+
async function fetchAppsCatalog(options = {}) {
|
|
195
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
196
|
+
const logger = options.logger ?? console;
|
|
197
|
+
const appName = options.appName ?? options.currentAppKey;
|
|
198
|
+
const baseUrl = stripTrailingSlash(options.appsCatalogUrl ?? DEFAULT_APPS_CATALOG_URL);
|
|
199
|
+
const limit = typeof options.limit === "number" ? options.limit : 3;
|
|
200
|
+
const params = new URLSearchParams();
|
|
201
|
+
if (appName) params.set("appName", appName);
|
|
202
|
+
if (limit > 0) params.set("limit", String(limit));
|
|
203
|
+
const url = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
|
|
204
|
+
if (!fetchImpl) {
|
|
205
|
+
throw new Error("Fetch implementation is required");
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const response = await fetchImpl(url);
|
|
209
|
+
const text = await response.text();
|
|
210
|
+
const parsed = safeJsonParse(text);
|
|
211
|
+
if (!response.ok || !parsed) {
|
|
212
|
+
logger?.error?.("Failed to fetch apps catalog", { status: response.status });
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
return parsed;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
logger?.error?.("Failed to fetch apps catalog", {
|
|
218
|
+
error: error instanceof Error ? error.message : String(error)
|
|
219
|
+
});
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function getCrossSellOffersFromApi(options = {}) {
|
|
224
|
+
const appsCatalog = options.appsCatalog ?? await fetchAppsCatalog(options);
|
|
225
|
+
return getCrossSellOffers({ ...options, appsCatalog });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/ui/PartnerIdCard.tsx
|
|
229
|
+
import { FormLayout, LegacyCard, SkeletonBodyText, TextField } from "@shopify/polaris";
|
|
230
|
+
import { jsx } from "react/jsx-runtime";
|
|
231
|
+
function PartnerIdCard({
|
|
232
|
+
partnerId,
|
|
233
|
+
onPartnerIdChange,
|
|
234
|
+
onSave,
|
|
235
|
+
saveLabel,
|
|
236
|
+
loading,
|
|
237
|
+
saving,
|
|
238
|
+
locked,
|
|
239
|
+
error,
|
|
240
|
+
label,
|
|
241
|
+
placeholder,
|
|
242
|
+
helpText,
|
|
243
|
+
saveDisabled
|
|
244
|
+
}) {
|
|
245
|
+
return /* @__PURE__ */ jsx(
|
|
246
|
+
LegacyCard,
|
|
247
|
+
{
|
|
248
|
+
sectioned: true,
|
|
249
|
+
primaryFooterAction: {
|
|
250
|
+
content: saveLabel,
|
|
251
|
+
loading: Boolean(saving),
|
|
252
|
+
onAction: onSave,
|
|
253
|
+
disabled: Boolean(saveDisabled)
|
|
254
|
+
},
|
|
255
|
+
children: loading ? /* @__PURE__ */ jsx(SkeletonBodyText, { lines: 1 }) : /* @__PURE__ */ jsx(FormLayout, { children: /* @__PURE__ */ jsx(
|
|
256
|
+
TextField,
|
|
257
|
+
{
|
|
258
|
+
error,
|
|
259
|
+
label,
|
|
260
|
+
value: partnerId,
|
|
261
|
+
onChange: onPartnerIdChange,
|
|
262
|
+
placeholder,
|
|
263
|
+
helpText,
|
|
264
|
+
autoComplete: "off",
|
|
265
|
+
disabled: Boolean(locked || loading)
|
|
266
|
+
}
|
|
267
|
+
) })
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/ui/Recommendations.tsx
|
|
273
|
+
import { ActionList, Badge, Button, Layout, LegacyCard as LegacyCard2, Popover, Text } from "@shopify/polaris";
|
|
274
|
+
import { ExternalIcon, MenuHorizontalIcon } from "@shopify/polaris-icons";
|
|
275
|
+
import { useState } from "react";
|
|
276
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
277
|
+
function Recommendations({
|
|
278
|
+
recommendations,
|
|
279
|
+
loading,
|
|
280
|
+
title,
|
|
281
|
+
dismissLabel,
|
|
282
|
+
installLabel,
|
|
283
|
+
freePlanLabel,
|
|
284
|
+
newBadgeLabel,
|
|
285
|
+
onDismiss,
|
|
286
|
+
openUrl,
|
|
287
|
+
getDescription
|
|
288
|
+
}) {
|
|
289
|
+
const [menuActive, setMenuActive] = useState(false);
|
|
290
|
+
if (!recommendations || recommendations.length === 0) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
const handleOpenUrl = (url) => {
|
|
294
|
+
if (openUrl) return openUrl(url);
|
|
295
|
+
if (typeof window !== "undefined") {
|
|
296
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
return /* @__PURE__ */ jsx2(
|
|
300
|
+
LegacyCard2,
|
|
301
|
+
{
|
|
302
|
+
sectioned: true,
|
|
303
|
+
title: /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
304
|
+
/* @__PURE__ */ jsx2(Text, { variant: "headingSm", as: "h3", children: title }),
|
|
305
|
+
/* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2(
|
|
306
|
+
Popover,
|
|
307
|
+
{
|
|
308
|
+
active: menuActive,
|
|
309
|
+
activator: /* @__PURE__ */ jsx2(
|
|
310
|
+
Button,
|
|
311
|
+
{
|
|
312
|
+
onClick: () => setMenuActive(!menuActive),
|
|
313
|
+
icon: MenuHorizontalIcon,
|
|
314
|
+
variant: "plain",
|
|
315
|
+
accessibilityLabel: "Menu"
|
|
316
|
+
}
|
|
317
|
+
),
|
|
318
|
+
onClose: () => setMenuActive(false),
|
|
319
|
+
preferredAlignment: "right",
|
|
320
|
+
children: /* @__PURE__ */ jsx2(
|
|
321
|
+
ActionList,
|
|
322
|
+
{
|
|
323
|
+
actionRole: "menuitem",
|
|
324
|
+
items: [
|
|
325
|
+
{
|
|
326
|
+
content: dismissLabel,
|
|
327
|
+
onAction: () => {
|
|
328
|
+
setMenuActive(false);
|
|
329
|
+
onDismiss();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
) })
|
|
337
|
+
] }),
|
|
338
|
+
children: /* @__PURE__ */ jsx2(Layout, { children: recommendations.map((rec, index) => /* @__PURE__ */ jsx2(Layout.Section, { variant: "oneThird", children: /* @__PURE__ */ jsx2(LegacyCard2, { children: /* @__PURE__ */ jsxs(LegacyCard2.Section, { children: [
|
|
339
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "16px", position: "relative" }, children: [
|
|
340
|
+
/* @__PURE__ */ jsx2(
|
|
341
|
+
"div",
|
|
342
|
+
{
|
|
343
|
+
style: {
|
|
344
|
+
flexShrink: 0,
|
|
345
|
+
width: "48px",
|
|
346
|
+
height: "48px",
|
|
347
|
+
borderRadius: "12px",
|
|
348
|
+
overflow: "hidden",
|
|
349
|
+
backgroundColor: "#f6f6f7",
|
|
350
|
+
display: "flex",
|
|
351
|
+
alignItems: "center",
|
|
352
|
+
justifyContent: "center"
|
|
353
|
+
},
|
|
354
|
+
children: rec.icon ? /* @__PURE__ */ jsx2(
|
|
355
|
+
"img",
|
|
356
|
+
{
|
|
357
|
+
src: rec.icon,
|
|
358
|
+
alt: rec.title,
|
|
359
|
+
style: { width: "48px", height: "48px", objectFit: "cover" }
|
|
360
|
+
}
|
|
361
|
+
) : /* @__PURE__ */ jsx2(
|
|
362
|
+
"div",
|
|
363
|
+
{
|
|
364
|
+
style: {
|
|
365
|
+
width: "48px",
|
|
366
|
+
height: "48px",
|
|
367
|
+
backgroundColor: "#e1e1e1",
|
|
368
|
+
borderRadius: "12px"
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
)
|
|
372
|
+
}
|
|
373
|
+
),
|
|
374
|
+
/* @__PURE__ */ jsx2("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
375
|
+
rec.newBadge && newBadgeLabel && /* @__PURE__ */ jsx2("div", { style: { position: "absolute", right: "-8px", top: "-8px" }, children: /* @__PURE__ */ jsx2(Badge, { tone: "info", children: newBadgeLabel }) }),
|
|
376
|
+
/* @__PURE__ */ jsx2(Text, { variant: "headingSm", as: "h3", truncate: true, children: rec.title }),
|
|
377
|
+
freePlanLabel && /* @__PURE__ */ jsx2("div", { style: { marginTop: "6px" }, children: /* @__PURE__ */ jsx2(Badge, { tone: "info", children: freePlanLabel }) })
|
|
378
|
+
] }) })
|
|
379
|
+
] }),
|
|
380
|
+
/* @__PURE__ */ jsx2("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ jsx2(Text, { variant: "bodySm", tone: "subdued", as: "p", children: getDescription ? getDescription(rec) : rec.desc }) }),
|
|
381
|
+
/* @__PURE__ */ jsx2("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ jsx2(
|
|
382
|
+
Button,
|
|
383
|
+
{
|
|
384
|
+
disabled: loading,
|
|
385
|
+
size: "micro",
|
|
386
|
+
variant: "plain",
|
|
387
|
+
onClick: () => handleOpenUrl(rec.url),
|
|
388
|
+
icon: ExternalIcon,
|
|
389
|
+
children: installLabel
|
|
390
|
+
}
|
|
391
|
+
) })
|
|
392
|
+
] }) }) }, `${rec.key}-${index}`)) })
|
|
393
|
+
}
|
|
394
|
+
);
|
|
221
395
|
}
|
|
222
|
-
var appsCatalog = catalog;
|
|
223
396
|
export {
|
|
224
|
-
|
|
397
|
+
PartnerIdCard,
|
|
398
|
+
Recommendations,
|
|
225
399
|
createProgusConnector,
|
|
400
|
+
fetchAppsCatalog,
|
|
226
401
|
getCrossSellOffers,
|
|
402
|
+
getCrossSellOffersFromApi,
|
|
227
403
|
normalizePartnerId,
|
|
228
404
|
signPayload
|
|
229
405
|
};
|
package/package.json
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@progus/connector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Progus partner/affiliate connector helpers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.cjs",
|
|
8
|
-
"module": "./dist/index.
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
|
-
"import": "./dist/index.
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
14
|
"require": "./dist/index.cjs"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist",
|
|
19
|
-
"README.md"
|
|
20
|
-
"src/appsCatalog.json"
|
|
19
|
+
"README.md"
|
|
21
20
|
],
|
|
22
21
|
"sideEffects": false,
|
|
23
22
|
"engines": {
|
|
@@ -27,12 +26,21 @@
|
|
|
27
26
|
"access": "public"
|
|
28
27
|
},
|
|
29
28
|
"scripts": {
|
|
30
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --clean --target node18",
|
|
31
|
-
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --target node18",
|
|
29
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean --target node18 --external react --external react-dom --external @shopify/polaris --external @shopify/polaris-icons",
|
|
30
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --target node18 --external react --external react-dom --external @shopify/polaris --external @shopify/polaris-icons",
|
|
31
|
+
"prepare": "npm run build",
|
|
32
32
|
"smoke": "tsx scripts/smoke.ts"
|
|
33
33
|
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@shopify/polaris": ">=13",
|
|
36
|
+
"@shopify/polaris-icons": ">=8",
|
|
37
|
+
"react": ">=18",
|
|
38
|
+
"react-dom": ">=18"
|
|
39
|
+
},
|
|
34
40
|
"devDependencies": {
|
|
35
41
|
"@types/node": "^20.11.30",
|
|
42
|
+
"@types/react": "^18.2.66",
|
|
43
|
+
"@types/react-dom": "^18.2.22",
|
|
36
44
|
"tsup": "^8.0.1",
|
|
37
45
|
"tsx": "^4.7.1",
|
|
38
46
|
"typescript": "^5.4.5"
|
package/src/appsCatalog.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"key": "progus_cod",
|
|
4
|
-
"type": "app",
|
|
5
|
-
"title": "Progus COD",
|
|
6
|
-
"company": "Progus",
|
|
7
|
-
"companyUrl": "https://progus.com",
|
|
8
|
-
"desc": "Automate COD Fees & Hide/Show Cash on Delivery by Rules",
|
|
9
|
-
"url": "https://apps.shopify.com/progus-cod",
|
|
10
|
-
"icon": "https://cdn.shopify.com/app-store/listing_images/bc537219cc3ed2bd4e7e3e683fe6b74a/icon/CMi_6dTEkIoDEAE=.png",
|
|
11
|
-
"priority": 100,
|
|
12
|
-
"enabled": true
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"key": "progus_trust_badges",
|
|
16
|
-
"type": "app",
|
|
17
|
-
"title": "Progus Trust Badges",
|
|
18
|
-
"company": "Progus",
|
|
19
|
-
"companyUrl": "https://progus.com",
|
|
20
|
-
"desc": "Add Trust Badges to your store to build trust and credibility.",
|
|
21
|
-
"url": "https://apps.shopify.com/progus-trust-badges-1",
|
|
22
|
-
"icon": "https://cdn.shopify.com/app-store/listing_images/f9d0009e237f27d2db35b41ef99be858/icon/CJ3y1qDn1JEDEAE=.png",
|
|
23
|
-
"priority": 90,
|
|
24
|
-
"enabled": true
|
|
25
|
-
}
|
|
26
|
-
]
|