@nosto/nosto-react 0.1.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.
@@ -0,0 +1,29 @@
1
+ import React, { useEffect } from "react";
2
+
3
+ const NostoHome: React.FC = () => {
4
+ useEffect(() => {
5
+ // @ts-ignore
6
+ window.nostojs((api) => {
7
+ api
8
+ .defaultSession()
9
+ .setResponseMode("HTML")
10
+ .viewFrontPage()
11
+ .setPlacements(api.placements.getPlacements())
12
+ .load()
13
+ .then((data: object) => {
14
+ // @ts-ignore
15
+ api.placements.injectCampaigns(data.recommendations);
16
+ });
17
+ });
18
+ }, []);
19
+
20
+ return (
21
+ <>
22
+ <div className="nosto_page_type" style={{ display: "none" }}>
23
+ front
24
+ </div>
25
+ </>
26
+ );
27
+ };
28
+
29
+ export default NostoHome;
@@ -0,0 +1,38 @@
1
+ import { Purchase } from "../../types";
2
+ import React, { useEffect } from "react";
3
+ import snakeize from "snakeize";
4
+
5
+ export interface OrderProps {
6
+ purchase: Purchase;
7
+ }
8
+
9
+ const NostoOrder: React.FC<{ order: OrderProps }> = ({ order }) => {
10
+ useEffect(() => {
11
+ // @ts-ignore
12
+ window.nostojs((api) => {
13
+ api
14
+ .defaultSession()
15
+ .setResponseMode("HTML")
16
+ .addOrder(snakeize(order))
17
+ .setPlacements(api.placements.getPlacements())
18
+ .load()
19
+ .then((data: object) => {
20
+ // @ts-ignore
21
+ api.placements.injectCampaigns(data.recommendations);
22
+ });
23
+ });
24
+ }, []);
25
+
26
+ return (
27
+ <>
28
+ <div className="nosto_page_type" style={{ display: "none" }}>
29
+ order
30
+ </div>
31
+ <div className="nosto_order" style={{ display: "none" }}>
32
+ {order.purchase.number}
33
+ </div>
34
+ </>
35
+ );
36
+ };
37
+
38
+ export default NostoOrder;
@@ -0,0 +1,29 @@
1
+ import React, { useEffect } from "react";
2
+
3
+ const NostoOther: React.FC = () => {
4
+ useEffect(() => {
5
+ // @ts-ignore
6
+ window.nostojs((api) => {
7
+ api
8
+ .defaultSession()
9
+ .setResponseMode("HTML")
10
+ .viewOther()
11
+ .setPlacements(api.placements.getPlacements())
12
+ .load()
13
+ .then((data: object) => {
14
+ // @ts-ignore
15
+ api.placements.injectCampaigns(data.recommendations);
16
+ });
17
+ });
18
+ }, []);
19
+
20
+ return (
21
+ <>
22
+ <div className="nosto_page_type" style={{ display: "none" }}>
23
+ other
24
+ </div>
25
+ </>
26
+ );
27
+ };
28
+
29
+ export default NostoOther;
@@ -0,0 +1,25 @@
1
+ import React, { MouseEvent } from "react";
2
+
3
+ export interface PlacementProps {
4
+ id: string;
5
+ }
6
+
7
+ const NostoPlacement: React.FC<PlacementProps> = ({ id }) => {
8
+ const handleClick = (e: MouseEvent) => {
9
+ // noinspection TypeScriptUnresolvedFunction
10
+ // @ts-ignore
11
+ const targetLink = e.target.closest("a");
12
+ if (!targetLink) {
13
+ return;
14
+ } else {
15
+ e.preventDefault();
16
+ location.href = targetLink.href
17
+ .toString()
18
+ .replace(new URL(targetLink.href).origin, "");
19
+ }
20
+ };
21
+
22
+ return <div className="nosto_element" onClick={handleClick} id={id} />;
23
+ };
24
+
25
+ export default NostoPlacement;
@@ -0,0 +1,150 @@
1
+ import { Product } from "../../types";
2
+ import stringinate from "../../utils/stringinate";
3
+ import React, { useEffect } from "react";
4
+ import snakeize from "snakeize";
5
+
6
+ const NostoProduct: React.FC<{ product: string; tagging: Product }> = ({
7
+ product,
8
+ tagging,
9
+ }) => {
10
+ useEffect(() => {
11
+ // @ts-ignore
12
+ window.nostojs((api) => {
13
+ api
14
+ .defaultSession()
15
+ .setResponseMode("HTML")
16
+ .viewProduct(snakeize(stringinate(tagging)))
17
+ .setPlacements(api.placements.getPlacements())
18
+ .load()
19
+ .then((data: object) => {
20
+ // @ts-ignore
21
+ api.placements.injectCampaigns(data.recommendations);
22
+ });
23
+ });
24
+ }, []);
25
+
26
+ return (
27
+ <>
28
+ <div className="nosto_page_type" style={{ display: "none" }}>
29
+ product
30
+ </div>
31
+ <div className="nosto_product" style={{ display: "none" }}>
32
+ {tagging.variationId && (
33
+ <span className="variation_id">{tagging.variationId}</span>
34
+ )}
35
+ {tagging.productId && (
36
+ <span className="product_id">{tagging.productId}</span>
37
+ )}
38
+ {tagging.name && <span className="name">{tagging.name}</span>}
39
+ {tagging.url && <span className="url">{tagging.url.toString()}</span>}
40
+ {tagging.imageUrl && (
41
+ <span className="image_url">{tagging.imageUrl.toString()}</span>
42
+ )}
43
+ {tagging.availability && (
44
+ <span className="availability">{tagging.availability}</span>
45
+ )}
46
+ {tagging.price && <span className="price">{tagging.price}</span>}
47
+ {tagging.listPrice && (
48
+ <span className="list_price">{tagging.listPrice}</span>
49
+ )}
50
+ {tagging.priceCurrencyCode && (
51
+ <span className="price_currency_code">
52
+ {tagging.priceCurrencyCode}
53
+ </span>
54
+ )}
55
+ {tagging.brand && <span className="brand">{tagging.brand}</span>}
56
+ {tagging.description && (
57
+ <span className="description">{tagging.description}</span>
58
+ )}
59
+ {tagging.googleCategory && (
60
+ <span className="description">{tagging.googleCategory}</span>
61
+ )}
62
+ {tagging.condition && (
63
+ <span className="condition">{tagging.condition}</span>
64
+ )}
65
+ {tagging.gender && <span className="gender">{tagging.gender}</span>}
66
+ {tagging.ageGroup && (
67
+ <span className="age_group">{tagging.ageGroup}</span>
68
+ )}
69
+ {tagging.gtin && <span className="gtin">{tagging.gtin}</span>}
70
+ {tagging.category &&
71
+ tagging.category.map((category, index) => (
72
+ <span className="category" key={index}>
73
+ {category}
74
+ </span>
75
+ ))}
76
+ {tagging.tags1 &&
77
+ tagging.tags1.map((tag, index) => (
78
+ <span className="tag1" key={index}>
79
+ {tag}
80
+ </span>
81
+ ))}
82
+ {tagging.tags2 &&
83
+ tagging.tags2.map((tag, index) => (
84
+ <span className="tag2" key={index}>
85
+ {tag}
86
+ </span>
87
+ ))}
88
+ {tagging.tags3 &&
89
+ tagging.tags3.map((tag, index) => (
90
+ <span className="tag3" key={index}>
91
+ {tag}
92
+ </span>
93
+ ))}
94
+ {tagging.ratingValue && (
95
+ <span className="rating_value">{tagging.ratingValue}</span>
96
+ )}
97
+ {tagging.reviewCount && (
98
+ <span className="review_count">{tagging.reviewCount}</span>
99
+ )}
100
+ {tagging.alternateImageUrls &&
101
+ tagging.alternateImageUrls.map((url, index) => (
102
+ <span className="alternate_image_url" key={index}>
103
+ {url.toString()}
104
+ </span>
105
+ ))}
106
+ {tagging.customFields &&
107
+ Object.keys(tagging.customFields).map(
108
+ (field, index) =>
109
+ tagging.customFields &&
110
+ tagging.customFields[field] && (
111
+ <span className={field} key={index}>
112
+ {tagging.customFields[field]}
113
+ </span>
114
+ )
115
+ )}
116
+ {tagging.skus &&
117
+ tagging.skus.map((sku, index) => (
118
+ <span className="nosto_sku" key={index}>
119
+ {sku.id && <span className="product_id">{sku.id}</span>}
120
+ {sku.name && <span className="name">{sku.name}</span>}
121
+ {sku.price && <span className="price">{sku.price}</span>}
122
+ {sku.listPrice && (
123
+ <span className="list_price">{sku.listPrice}</span>
124
+ )}
125
+ {sku.url && <span className="url">{sku.url.toString()}</span>}
126
+ {sku.imageUrl && (
127
+ <span className="image_url">{sku.imageUrl.toString()}</span>
128
+ )}
129
+ {sku.gtin && <span className="gtin">{sku.gtin}</span>}
130
+ {sku.availability && (
131
+ <span className="availability">{sku.availability}</span>
132
+ )}
133
+ {sku.customFields &&
134
+ Object.keys(sku.customFields).map(
135
+ (field, index) =>
136
+ sku.customFields &&
137
+ sku.customFields[field] && (
138
+ <span className={field} key={index}>
139
+ {sku.customFields[field]}
140
+ </span>
141
+ )
142
+ )}
143
+ </span>
144
+ ))}
145
+ </div>
146
+ </>
147
+ );
148
+ };
149
+
150
+ export default NostoProduct;
@@ -0,0 +1,12 @@
1
+ import { createContext } from "react";
2
+
3
+ export interface NostoInterface {
4
+ account: string;
5
+ }
6
+
7
+ /* tslint:disable:no-empty */
8
+ export const NostoContext = createContext<NostoInterface>({
9
+ // @ts-ignore
10
+ account: undefined
11
+ });
12
+ /* tslint:enable:no-empty */
@@ -0,0 +1,35 @@
1
+ import React, { useEffect } from "react";
2
+ import { NostoContext } from "./context";
3
+
4
+ interface NostoProviderProps {
5
+ account: string;
6
+ host: string;
7
+ children: React.ReactElement;
8
+ }
9
+
10
+ const NostoProvider: React.FC<NostoProviderProps> = ({
11
+ account,
12
+ host,
13
+ children,
14
+ }) => {
15
+ useEffect(() => {
16
+ const script = document.createElement("script");
17
+ script.type = "text/javascript";
18
+ script.src = "//" + (host || "connect.nosto.com") + "/include/" + account;
19
+ script.async = true;
20
+ document.head.appendChild(script);
21
+
22
+ window.nostojs = (cb: Function) =>
23
+ (window.nostojs.q = window.nostojs.q || []).push(cb);
24
+ // @ts-ignore
25
+ window.nostojs((api) => api.setAutoLoad(false));
26
+ }, []);
27
+
28
+ return (
29
+ <NostoContext.Provider value={{ account }}>
30
+ {children}
31
+ </NostoContext.Provider>
32
+ );
33
+ };
34
+
35
+ export default NostoProvider;
@@ -0,0 +1,32 @@
1
+ import React, { useEffect } from "react";
2
+
3
+ const NostoSearch: React.FC<{ query: string }> = ({ query }) => {
4
+ useEffect(() => {
5
+ // @ts-ignore
6
+ window.nostojs((api) => {
7
+ api
8
+ .defaultSession()
9
+ .setResponseMode("HTML")
10
+ .viewSearch(query)
11
+ .setPlacements(api.placements.getPlacements())
12
+ .load()
13
+ .then((data: object) => {
14
+ // @ts-ignore
15
+ api.placements.injectCampaigns(data.recommendations);
16
+ });
17
+ });
18
+ }, []);
19
+
20
+ return (
21
+ <>
22
+ <div className="nosto_page_type" style={{ display: "none" }}>
23
+ search
24
+ </div>
25
+ <div className="nosto_search" style={{ display: "none" }}>
26
+ {query}
27
+ </div>
28
+ </>
29
+ );
30
+ };
31
+
32
+ export default NostoSearch;
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import snakeize from "snakeize";
3
+
4
+ import useDeepCompareEffect from "use-deep-compare-effect";
5
+ import { Cart, Customer } from "../../types";
6
+
7
+ interface NostoSessionProps {
8
+ cart: Cart;
9
+ customer: Customer;
10
+ }
11
+
12
+ const NostoSession: React.FC<NostoSessionProps> = ({ cart, customer }) => {
13
+ useDeepCompareEffect(() => {
14
+ const currentUser = customer ? customer : undefined;
15
+ console.debug(currentUser);
16
+ const currentCart = cart ? cart : undefined;
17
+ console.debug(currentCart);
18
+
19
+ // @ts-ignore
20
+ window.nostojs((api) => {
21
+ api
22
+ .defaultSession()
23
+ .setResponseMode("HTML")
24
+ .setCart(snakeize(currentCart))
25
+ .viewOther()
26
+ .load();
27
+ });
28
+ }, [cart || [], customer || {}]);
29
+
30
+ return <div />;
31
+ };
32
+
33
+ export default NostoSession;
@@ -0,0 +1,31 @@
1
+ declare global {
2
+ // noinspection JSUnusedGlobalSymbols
3
+ interface Window {
4
+ nostojs: any;
5
+ nosto: any;
6
+ }
7
+ }
8
+
9
+ export * from "./types";
10
+ // noinspection JSUnusedGlobalSymbols
11
+ export { default as Nosto404 } from "./components/Fohofo";
12
+ // noinspection JSUnusedGlobalSymbols
13
+ export { default as NostoOther } from "./components/Other";
14
+ // noinspection JSUnusedGlobalSymbols
15
+ export { default as NostoCheckout } from "./components/Checkout";
16
+ // noinspection JSUnusedGlobalSymbols
17
+ export { default as NostoProduct } from "./components/Product";
18
+ // noinspection JSUnusedGlobalSymbols
19
+ export { default as NostoCategory } from "./components/Category";
20
+ // noinspection JSUnusedGlobalSymbols
21
+ export { default as NostoSearch } from "./components/Search";
22
+ // noinspection JSUnusedGlobalSymbols
23
+ export { default as NostoOrder } from "./components/Order";
24
+ // noinspection JSUnusedGlobalSymbols
25
+ export { default as NostoHome } from "./components/Home";
26
+ // noinspection JSUnusedGlobalSymbols
27
+ export { default as NostoPlacement } from "./components/Placement";
28
+ // noinspection JSUnusedGlobalSymbols
29
+ export { default as NostoProvider } from "./components/Provider";
30
+ // noinspection JSUnusedGlobalSymbols
31
+ export { default as NostoSession } from "./components/Session";
@@ -0,0 +1 @@
1
+ declare module "snakeize";
package/src/types.ts ADDED
@@ -0,0 +1,76 @@
1
+ export interface Item {
2
+ name: string;
3
+ price_currency_code: string;
4
+ product_id: string;
5
+ quantity: number;
6
+ sku_id: string;
7
+ unit_price: number;
8
+ }
9
+
10
+ export interface Cart {
11
+ items: Item[];
12
+ }
13
+
14
+ export interface Customer {
15
+ customer_reference: string;
16
+ email: string;
17
+ first_name: string;
18
+ last_name: string;
19
+ newsletter: boolean;
20
+ }
21
+
22
+ export interface Buyer {
23
+ first_name: string;
24
+ last_name: string;
25
+ email: string;
26
+ type: string;
27
+ newsletter: boolean;
28
+ }
29
+
30
+ export interface Purchase {
31
+ number: string;
32
+ info: Buyer;
33
+ items: Item[];
34
+ }
35
+
36
+ export interface SKU {
37
+ id: string;
38
+ name: string;
39
+ price: number;
40
+ listPrice?: number;
41
+ url: URL;
42
+ imageUrl: URL;
43
+ gtin?: string;
44
+ availability: 'InStock' | 'OutOfStock';
45
+ customFields?: { [key: string]: string };
46
+ }
47
+
48
+ export interface Product {
49
+ alternateImageUrls?: URL[];
50
+ availability: 'InStock' | 'OutOfStock';
51
+ brand?: string;
52
+ category: string[];
53
+ categoryIds?: string[];
54
+ description: string;
55
+ googleCategory?: string;
56
+ condition?: string;
57
+ gender?: string;
58
+ ageGroup?: string;
59
+ gtin?: string;
60
+ imageUrl: URL;
61
+ listPrice?: number;
62
+ name: string;
63
+ price: number;
64
+ ratingValue?: number;
65
+ reviewCount?: number;
66
+ priceCurrencyCode: string;
67
+ productId: string;
68
+ tags1?: string[];
69
+ tags2?: string[];
70
+ tags3?: string[];
71
+ thumbUrl?: URL;
72
+ url: URL;
73
+ customFields?: { [key: string]: string };
74
+ variationId?: string;
75
+ skus?: SKU[];
76
+ }
@@ -0,0 +1,16 @@
1
+ export default function stringinate(obj: {
2
+ [key: string]: any;
3
+ }): { [key: string]: any } {
4
+ Object.keys(obj).forEach((property) => {
5
+ if (obj[property] instanceof URL) {
6
+ obj[property] = obj[property].toString();
7
+ }
8
+ if (typeof obj[property] === "object") {
9
+ stringinate(obj[property]);
10
+ }
11
+
12
+ obj[property] = obj[property];
13
+ });
14
+
15
+ return obj;
16
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />