@pixelated-tech/components 3.8.0 → 3.9.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 +7 -0
- package/dist/components/admin/site-health/site-health-axe-core.integration.js +135 -20
- package/dist/components/admin/site-health/site-health-axe-core.integration.test.js +79 -0
- package/dist/components/admin/site-health/site-health-axe-core.js +35 -0
- package/dist/components/admin/site-health/site-health-core-web-vitals.integration.js +5 -3
- package/dist/components/admin/site-health/site-health-core-web-vitals.integration.test.js +33 -0
- package/dist/components/admin/site-health/site-health-template.js +3 -1
- package/dist/components/admin/site-health/site-health-utils.js +22 -0
- package/dist/components/config/config.example.js +2 -0
- package/dist/components/config/config.types.js +1 -3
- package/dist/components/config/config.utils.js +13 -5
- package/dist/components/config/config.validators.js +67 -0
- package/dist/components/general/cache-manager.js +124 -0
- package/dist/components/general/google.reviews.components.js +1 -2
- package/dist/components/general/googlemap.js +5 -2
- package/dist/components/general/metadata.functions.js +15 -1
- package/dist/components/general/proxy-csp-listener.js +20 -0
- package/dist/components/general/proxy-handler.js +4 -2
- package/dist/components/general/sitemap.js +9 -16
- package/dist/components/shoppingcart/ebay.components.js +123 -15
- package/dist/components/shoppingcart/ebay.functions.js +136 -34
- package/dist/components/shoppingcart/shoppingcart.components.js +4 -2
- package/dist/components/sitebuilder/config/ConfigEngine.js +5 -2
- package/dist/components/sitebuilder/config/google-fonts.js +5 -3
- package/dist/components/sitebuilder/page/lib/pageStorageLocal.js +2 -1
- package/dist/config/pixelated.config.json +83 -69
- package/dist/index.adminclient.js +1 -0
- package/dist/index.js +3 -0
- package/dist/index.server.js +1 -0
- package/dist/scripts/release.sh +2 -2
- package/dist/test/config.mock.js +13 -0
- package/dist/test/setup.js +46 -0
- package/dist/test/test-utils.js +23 -0
- package/dist/types/components/admin/site-health/site-health-axe-core.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-axe-core.integration.d.ts +2 -0
- package/dist/types/components/admin/site-health/site-health-axe-core.integration.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-axe-core.integration.test.d.ts +2 -0
- package/dist/types/components/admin/site-health/site-health-axe-core.integration.test.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-core-web-vitals.integration.d.ts +1 -0
- package/dist/types/components/admin/site-health/site-health-core-web-vitals.integration.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-core-web-vitals.integration.test.d.ts +2 -0
- package/dist/types/components/admin/site-health/site-health-core-web-vitals.integration.test.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-template.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-utils.d.ts +1 -1
- package/dist/types/components/admin/site-health/site-health-utils.d.ts.map +1 -1
- package/dist/types/components/config/config.example.d.ts.map +1 -1
- package/dist/types/components/config/config.types.d.ts +30 -11
- package/dist/types/components/config/config.types.d.ts.map +1 -1
- package/dist/types/components/config/config.utils.d.ts.map +1 -1
- package/dist/types/components/config/config.validators.d.ts +17 -0
- package/dist/types/components/config/config.validators.d.ts.map +1 -0
- package/dist/types/components/general/cache-manager.d.ts +45 -0
- package/dist/types/components/general/cache-manager.d.ts.map +1 -0
- package/dist/types/components/general/google.reviews.components.d.ts.map +1 -1
- package/dist/types/components/general/googlemap.d.ts +1 -1
- package/dist/types/components/general/googlemap.d.ts.map +1 -1
- package/dist/types/components/general/metadata.functions.d.ts +2 -8
- package/dist/types/components/general/metadata.functions.d.ts.map +1 -1
- package/dist/types/components/general/proxy-csp-listener.d.ts +15 -0
- package/dist/types/components/general/proxy-csp-listener.d.ts.map +1 -0
- package/dist/types/components/general/proxy-handler.d.ts.map +1 -1
- package/dist/types/components/general/schema-localbusiness.d.ts.map +1 -1
- package/dist/types/components/general/schema-website.d.ts.map +1 -1
- package/dist/types/components/general/sitemap.d.ts.map +1 -1
- package/dist/types/components/shoppingcart/ebay.components.d.ts +9 -0
- package/dist/types/components/shoppingcart/ebay.components.d.ts.map +1 -1
- package/dist/types/components/shoppingcart/ebay.functions.d.ts +20 -21
- package/dist/types/components/shoppingcart/ebay.functions.d.ts.map +1 -1
- package/dist/types/components/shoppingcart/shoppingcart.components.d.ts +1 -1
- package/dist/types/components/shoppingcart/shoppingcart.components.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts +135 -0
- package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/config/ConfigEngine.d.ts +3 -2
- package/dist/types/components/sitebuilder/config/ConfigEngine.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/config/google-fonts.d.ts +1 -1
- package/dist/types/components/sitebuilder/config/google-fonts.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/page/lib/pageStorageLocal.d.ts.map +1 -1
- package/dist/types/index.adminclient.d.ts +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.server.d.ts +1 -0
- package/dist/types/stories/{seo/seo.404.stories.d.ts → general/404.stories.d.ts} +1 -2
- package/dist/types/stories/general/404.stories.d.ts.map +1 -0
- package/dist/types/stories/general/accordion.stories.d.ts +3 -3
- package/dist/types/stories/general/accordion.stories.d.ts.map +1 -1
- package/dist/types/stories/general/buzzword-bingo.stories.d.ts.map +1 -0
- package/dist/types/stories/{callout → general}/callout.many.stories.d.ts +0 -1
- package/dist/types/stories/general/callout.many.stories.d.ts.map +1 -0
- package/dist/types/stories/{callout → general}/callout.stories.d.ts +0 -1
- package/dist/types/stories/general/callout.stories.d.ts.map +1 -0
- package/dist/types/stories/general/carousel-hero.stories.d.ts +14 -0
- package/dist/types/stories/general/carousel-hero.stories.d.ts.map +1 -0
- package/dist/types/stories/{carousel → general}/carousel-reviews.stories.d.ts +0 -1
- package/dist/types/stories/general/carousel-reviews.stories.d.ts.map +1 -0
- package/dist/types/stories/general/carousel-workportfolio.stories.d.ts +14 -0
- package/dist/types/stories/general/carousel-workportfolio.stories.d.ts.map +1 -0
- package/dist/types/stories/{carousel → general}/carousel.stories.d.ts +1 -9
- package/dist/types/stories/general/carousel.stories.d.ts.map +1 -0
- package/dist/types/stories/general/contentful.item.stories.d.ts +12 -0
- package/dist/types/stories/general/contentful.item.stories.d.ts.map +1 -0
- package/dist/types/stories/general/contentful.items.stories.d.ts +10 -0
- package/dist/types/stories/general/contentful.items.stories.d.ts.map +1 -0
- package/dist/types/stories/{cms → general}/contentful.stories.d.ts +0 -1
- package/dist/types/stories/general/contentful.stories.d.ts.map +1 -0
- package/dist/types/stories/{seo/seo.faq-accordion.stories.d.ts → general/faq-accordion.stories.d.ts} +1 -1
- package/dist/types/stories/general/faq-accordion.stories.d.ts.map +1 -0
- package/dist/types/stories/{cms → general}/google.reviews.stories.d.ts +1 -2
- package/dist/types/stories/general/google.reviews.stories.d.ts.map +1 -0
- package/dist/types/stories/{seo/seo.googleanalytics.stories.d.ts → general/googleanalytics.stories.d.ts} +2 -4
- package/dist/types/stories/general/googleanalytics.stories.d.ts.map +1 -0
- package/dist/types/stories/{seo/seo.googlesearch.stories.d.ts → general/googlesearch.stories.d.ts} +1 -1
- package/dist/types/stories/general/googlesearch.stories.d.ts.map +1 -0
- package/dist/types/stories/{cms → general}/gravatar.stories.d.ts +0 -1
- package/dist/types/stories/general/gravatar.stories.d.ts.map +1 -0
- package/dist/types/stories/general/headers.stories.d.ts.map +1 -1
- package/dist/types/stories/{cms → general}/instagram.stories.d.ts +2 -2
- package/dist/types/stories/general/instagram.stories.d.ts.map +1 -0
- package/dist/types/stories/general/layout.stories.d.ts +9 -9
- package/dist/types/stories/{structured → general}/markdown.stories.d.ts +1 -2
- package/dist/types/stories/general/markdown.stories.d.ts.map +1 -0
- package/dist/types/stories/{menu → general}/menu-accordion.stories.d.ts +2 -2
- package/dist/types/stories/general/menu-accordion.stories.d.ts.map +1 -0
- package/dist/types/stories/{menu → general}/menu-expando.stories.d.ts +1 -1
- package/dist/types/stories/general/menu-expando.stories.d.ts.map +1 -0
- package/dist/types/stories/{menu → general}/menu-simple.stories.d.ts +1 -1
- package/dist/types/stories/general/menu-simple.stories.d.ts.map +1 -0
- package/dist/types/stories/{seo/seo.metadata.stories.d.ts → general/metadata.stories.d.ts} +1 -1
- package/dist/types/stories/general/metadata.stories.d.ts.map +1 -0
- package/dist/types/stories/general/microinteractions.stories.d.ts +0 -1
- package/dist/types/stories/general/microinteractions.stories.d.ts.map +1 -1
- package/dist/types/stories/general/modal.stories.d.ts +0 -1
- package/dist/types/stories/general/modal.stories.d.ts.map +1 -1
- package/dist/types/stories/general/nerdjoke.stories.d.ts.map +1 -0
- package/dist/types/stories/{structured → general}/recipe.stories.d.ts +1 -2
- package/dist/types/stories/general/recipe.stories.d.ts.map +1 -0
- package/dist/types/stories/{structured → general}/resume.stories.d.ts +1 -2
- package/dist/types/stories/{structured → general}/resume.stories.d.ts.map +1 -1
- package/dist/types/stories/{seo/seo.schema.stories.d.ts → general/schema.stories.d.ts} +1 -2
- package/dist/types/stories/general/schema.stories.d.ts.map +1 -0
- package/dist/types/stories/{seo/seo.sitemap.stories.d.ts → general/sitemap.stories.d.ts} +1 -1
- package/dist/types/stories/general/sitemap.stories.d.ts.map +1 -0
- package/dist/types/stories/general/smartimage.stories.d.ts.map +1 -1
- package/dist/types/stories/{structured → general}/socialcard.stories.d.ts +0 -1
- package/dist/types/stories/general/socialcard.stories.d.ts.map +1 -0
- package/dist/types/stories/general/splitscroll.stories.d.ts.map +1 -1
- package/dist/types/stories/{carousel → general}/tiles.stories.d.ts +0 -1
- package/dist/types/stories/general/tiles.stories.d.ts.map +1 -0
- package/dist/types/stories/{structured → general}/timeline.stories.d.ts +0 -1
- package/dist/types/stories/general/timeline.stories.d.ts.map +1 -0
- package/dist/types/stories/{cms → general}/wordpress.stories.d.ts +6 -2
- package/dist/types/stories/general/wordpress.stories.d.ts.map +1 -0
- package/dist/types/stories/shoppingcart/ebay.stories.d.ts +16 -0
- package/dist/types/stories/shoppingcart/ebay.stories.d.ts.map +1 -0
- package/dist/types/stories/shoppingcart/shoppingcart.ebay.item.stories.d.ts +1 -12
- package/dist/types/stories/shoppingcart/shoppingcart.ebay.item.stories.d.ts.map +1 -1
- package/dist/types/stories/shoppingcart/shoppingcart.ebay.items.stories.d.ts +1 -12
- package/dist/types/stories/shoppingcart/shoppingcart.ebay.items.stories.d.ts.map +1 -1
- package/dist/types/stories/shoppingcart/shoppingcart.stories.d.ts.map +1 -1
- package/dist/types/stories/sitebuilder/compoundfontselector.stories.d.ts +13 -35
- package/dist/types/stories/sitebuilder/compoundfontselector.stories.d.ts.map +1 -1
- package/dist/types/stories/sitebuilder/form-engine.stories.d.ts +0 -1
- package/dist/types/stories/sitebuilder/form-engine.stories.d.ts.map +1 -1
- package/dist/types/stories/sitebuilder/pageengine.stories.d.ts +0 -1
- package/dist/types/stories/sitebuilder/pageengine.stories.d.ts.map +1 -1
- package/dist/types/test/config.mock.d.ts +11 -0
- package/dist/types/test/config.mock.d.ts.map +1 -0
- package/dist/types/test/setup.d.ts.map +1 -0
- package/dist/types/test/test-utils.d.ts +87 -0
- package/dist/types/test/test-utils.d.ts.map +1 -0
- package/dist/types/tests/cache-manager.test.d.ts +2 -0
- package/dist/types/tests/cache-manager.test.d.ts.map +1 -0
- package/dist/types/tests/config-core.test.d.ts +2 -0
- package/dist/types/tests/config-core.test.d.ts.map +1 -0
- package/dist/types/tests/config.validators.test.d.ts +2 -0
- package/dist/types/tests/config.validators.test.d.ts.map +1 -0
- package/dist/types/tests/ebay-functions.test.d.ts +2 -0
- package/dist/types/tests/ebay-functions.test.d.ts.map +1 -0
- package/dist/types/tests/site-health-utils.test.d.ts +2 -0
- package/dist/types/tests/site-health-utils.test.d.ts.map +1 -0
- package/package.json +7 -7
- package/dist/types/stories/callout/callout.many.stories.d.ts.map +0 -1
- package/dist/types/stories/callout/callout.stories.d.ts.map +0 -1
- package/dist/types/stories/carousel/carousel-hero.stories.d.ts +0 -22
- package/dist/types/stories/carousel/carousel-hero.stories.d.ts.map +0 -1
- package/dist/types/stories/carousel/carousel-reviews.stories.d.ts.map +0 -1
- package/dist/types/stories/carousel/carousel-workportfolio.stories.d.ts +0 -22
- package/dist/types/stories/carousel/carousel-workportfolio.stories.d.ts.map +0 -1
- package/dist/types/stories/carousel/carousel.stories.d.ts.map +0 -1
- package/dist/types/stories/carousel/tiles.stories.d.ts.map +0 -1
- package/dist/types/stories/cms/contentful.item.stories.d.ts +0 -21
- package/dist/types/stories/cms/contentful.item.stories.d.ts.map +0 -1
- package/dist/types/stories/cms/contentful.items.stories.d.ts +0 -20
- package/dist/types/stories/cms/contentful.items.stories.d.ts.map +0 -1
- package/dist/types/stories/cms/contentful.stories.d.ts.map +0 -1
- package/dist/types/stories/cms/google.reviews.stories.d.ts.map +0 -1
- package/dist/types/stories/cms/gravatar.stories.d.ts.map +0 -1
- package/dist/types/stories/cms/instagram.stories.d.ts.map +0 -1
- package/dist/types/stories/cms/wordpress.stories.d.ts.map +0 -1
- package/dist/types/stories/menu/menu-accordion.stories.d.ts.map +0 -1
- package/dist/types/stories/menu/menu-expando.stories.d.ts.map +0 -1
- package/dist/types/stories/menu/menu-simple.stories.d.ts.map +0 -1
- package/dist/types/stories/nerdjoke.stories.d.ts.map +0 -1
- package/dist/types/stories/seo/seo.404.stories.d.ts.map +0 -1
- package/dist/types/stories/seo/seo.faq-accordion.stories.d.ts.map +0 -1
- package/dist/types/stories/seo/seo.googleanalytics.stories.d.ts.map +0 -1
- package/dist/types/stories/seo/seo.googlesearch.stories.d.ts.map +0 -1
- package/dist/types/stories/seo/seo.metadata.stories.d.ts.map +0 -1
- package/dist/types/stories/seo/seo.schema.stories.d.ts.map +0 -1
- package/dist/types/stories/seo/seo.sitemap.stories.d.ts.map +0 -1
- package/dist/types/stories/structured/buzzword-bingo.stories.d.ts.map +0 -1
- package/dist/types/stories/structured/markdown.stories.d.ts.map +0 -1
- package/dist/types/stories/structured/recipe.stories.d.ts.map +0 -1
- package/dist/types/stories/structured/socialcard.stories.d.ts.map +0 -1
- package/dist/types/stories/structured/timeline.stories.d.ts.map +0 -1
- package/dist/types/tests/setup.d.ts.map +0 -1
- /package/dist/types/stories/{structured → general}/buzzword-bingo.stories.d.ts +0 -0
- /package/dist/types/stories/{nerdjoke.stories.d.ts → general/nerdjoke.stories.d.ts} +0 -0
- /package/dist/types/{tests → test}/setup.d.ts +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CacheManager
|
|
3
|
+
*
|
|
4
|
+
* A unified caching utility that supports Memory, LocalStorage, and SessionStorage.
|
|
5
|
+
* Includes TTL (Time-To-Live) support and automatic SSR fallback.
|
|
6
|
+
*/
|
|
7
|
+
export class CacheManager {
|
|
8
|
+
memoryCache = new Map();
|
|
9
|
+
defaultTTL = 60 * 60 * 1000; // 1 hour
|
|
10
|
+
mode;
|
|
11
|
+
prefix;
|
|
12
|
+
ttl;
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.mode = options.mode || 'memory';
|
|
15
|
+
this.prefix = options.prefix || 'pix_';
|
|
16
|
+
this.ttl = options.ttl || this.defaultTTL;
|
|
17
|
+
// Fallback to memory if browser storage is requested but unavailable (SSR/Node environment)
|
|
18
|
+
if (typeof window === 'undefined' && (this.mode === 'local' || this.mode === 'session')) {
|
|
19
|
+
this.mode = 'memory';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Returns the storage engine based on the mode
|
|
24
|
+
*/
|
|
25
|
+
getStorage() {
|
|
26
|
+
if (typeof window === 'undefined')
|
|
27
|
+
return null;
|
|
28
|
+
if (this.mode === 'local')
|
|
29
|
+
return window.localStorage;
|
|
30
|
+
if (this.mode === 'session')
|
|
31
|
+
return window.sessionStorage;
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generates a prefixed key to avoid collisions
|
|
36
|
+
*/
|
|
37
|
+
getFullKey(key) {
|
|
38
|
+
return `${this.prefix}${key}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Retrieves data from the cache
|
|
42
|
+
*/
|
|
43
|
+
get(key) {
|
|
44
|
+
if (this.mode === 'none')
|
|
45
|
+
return null;
|
|
46
|
+
const fullKey = this.getFullKey(key);
|
|
47
|
+
let wrapper = null;
|
|
48
|
+
// 1. Check Memory cache first (fastest)
|
|
49
|
+
const memMatch = this.memoryCache.get(fullKey);
|
|
50
|
+
if (memMatch) {
|
|
51
|
+
wrapper = memMatch;
|
|
52
|
+
}
|
|
53
|
+
// 2. Check Browser storage if memory failed or was bypassed
|
|
54
|
+
if (!wrapper) {
|
|
55
|
+
const storage = this.getStorage();
|
|
56
|
+
if (storage) {
|
|
57
|
+
const raw = storage.getItem(fullKey);
|
|
58
|
+
if (raw) {
|
|
59
|
+
try {
|
|
60
|
+
wrapper = JSON.parse(raw);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// 3. Validate Expiry
|
|
69
|
+
if (wrapper) {
|
|
70
|
+
if (Date.now() < wrapper.expiry) {
|
|
71
|
+
// Resync memory cache if we pulled from storage
|
|
72
|
+
if (!memMatch) {
|
|
73
|
+
this.memoryCache.set(fullKey, wrapper);
|
|
74
|
+
}
|
|
75
|
+
return wrapper.data;
|
|
76
|
+
}
|
|
77
|
+
// Clean up expired items
|
|
78
|
+
this.remove(key);
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Stores data in the cache with a specified TTL
|
|
84
|
+
*/
|
|
85
|
+
set(key, data, customTTL) {
|
|
86
|
+
if (this.mode === 'none')
|
|
87
|
+
return;
|
|
88
|
+
const fullKey = this.getFullKey(key);
|
|
89
|
+
const expiry = Date.now() + (customTTL || this.ttl);
|
|
90
|
+
const wrapper = { data, expiry };
|
|
91
|
+
// Always update memory
|
|
92
|
+
this.memoryCache.set(fullKey, wrapper);
|
|
93
|
+
// Update browser storage if applicable
|
|
94
|
+
const storage = this.getStorage();
|
|
95
|
+
if (storage) {
|
|
96
|
+
storage.setItem(fullKey, JSON.stringify(wrapper));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Removes a specific item from all storage engines
|
|
101
|
+
*/
|
|
102
|
+
remove(key) {
|
|
103
|
+
const fullKey = this.getFullKey(key);
|
|
104
|
+
this.memoryCache.delete(fullKey);
|
|
105
|
+
const storage = this.getStorage();
|
|
106
|
+
if (storage) {
|
|
107
|
+
storage.removeItem(fullKey);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Clears only the items belonging to this manager (by prefix)
|
|
112
|
+
*/
|
|
113
|
+
clear() {
|
|
114
|
+
this.memoryCache.clear();
|
|
115
|
+
const storage = this.getStorage();
|
|
116
|
+
if (storage) {
|
|
117
|
+
Object.keys(storage).forEach(k => {
|
|
118
|
+
if (k.startsWith(this.prefix)) {
|
|
119
|
+
storage.removeItem(k);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -6,7 +6,6 @@ import { SmartImage } from './smartimage';
|
|
|
6
6
|
import { getGoogleReviewsByPlaceId } from './google.reviews.functions';
|
|
7
7
|
import { usePixelatedConfig } from '../config/config.client';
|
|
8
8
|
import './google.reviews.css';
|
|
9
|
-
const GOOGLE_MAPS_API_KEY = 'AIzaSyBJVi0O9Ir9imRgINLZbojTifatX-Z4aUs';
|
|
10
9
|
GoogleReviewsCard.propTypes = {
|
|
11
10
|
placeId: PropTypes.string.isRequired,
|
|
12
11
|
language: PropTypes.string,
|
|
@@ -20,7 +19,7 @@ export function GoogleReviewsCard(props) {
|
|
|
20
19
|
const [reviews, setReviews] = useState([]);
|
|
21
20
|
const [loading, setLoading] = useState(true);
|
|
22
21
|
const [error, setError] = useState(null);
|
|
23
|
-
const apiKey = props.apiKey || config?.googleMaps?.apiKey ||
|
|
22
|
+
const apiKey = props.apiKey || config?.googleMaps?.apiKey || '';
|
|
24
23
|
const proxyBase = props.proxyBase || config?.global?.proxyUrl || undefined;
|
|
25
24
|
useEffect(() => {
|
|
26
25
|
(async () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
+
import { usePixelatedConfig } from "../config/config.client";
|
|
3
4
|
// https://developers.google.com/maps/documentation/embed/embedding-map
|
|
4
5
|
GoogleMaps.propTypes = {
|
|
5
6
|
title: PropTypes.string,
|
|
@@ -8,9 +9,11 @@ GoogleMaps.propTypes = {
|
|
|
8
9
|
frameBorder: PropTypes.string,
|
|
9
10
|
style: PropTypes.object,
|
|
10
11
|
map_mode: PropTypes.string.isRequired,
|
|
11
|
-
api_key: PropTypes.string
|
|
12
|
+
api_key: PropTypes.string,
|
|
12
13
|
parameters: PropTypes.string,
|
|
13
14
|
};
|
|
14
15
|
export function GoogleMaps(props) {
|
|
15
|
-
|
|
16
|
+
const config = usePixelatedConfig();
|
|
17
|
+
const apiKey = props.api_key || config?.googleMaps?.apiKey;
|
|
18
|
+
return (_jsx("div", { className: "gmap", suppressHydrationWarning: true, children: _jsx("iframe", { title: props.title || "Google Map", width: props.width || "600", height: props.height || "400", frameBorder: props.frameBorder || "0", style: props.style || { border: 0 }, referrerPolicy: "no-referrer-when-downgrade", src: `https://www.google.com/maps/embed/v1/${props.map_mode}?key=${apiKey}&${props.parameters}`, allowFullScreen: true }) }));
|
|
16
19
|
}
|
|
@@ -65,6 +65,8 @@ export function getAllRoutes(routes, key) {
|
|
|
65
65
|
return result;
|
|
66
66
|
}
|
|
67
67
|
export const getMetadata = (routes, key = "name", value = "Home") => {
|
|
68
|
+
// Validate the routes blob early to fail fast if invalid
|
|
69
|
+
assertRoutes(routes);
|
|
68
70
|
const foundObject = getRouteByKey(routes, key, value);
|
|
69
71
|
if (foundObject) {
|
|
70
72
|
const metadata = {
|
|
@@ -105,8 +107,20 @@ export function getAccordionMenuData(myRoutes) {
|
|
|
105
107
|
});
|
|
106
108
|
return menuItems;
|
|
107
109
|
}
|
|
110
|
+
import { assertSiteInfo, assertRoutes } from '../config/config.validators';
|
|
108
111
|
export function generateMetaTags(props) {
|
|
109
112
|
const { title, description, keywords, origin, url, site_name: prop_site_name, email: prop_email, image: prop_image, image_height: prop_image_height, image_width: prop_image_width, favicon: prop_favicon, siteInfo } = props;
|
|
113
|
+
const safeOrigin = typeof origin === 'string' && origin.length > 0 ? origin : '';
|
|
114
|
+
const safeUrl = typeof url === 'string' && url.length > 0 ? url : '';
|
|
115
|
+
let newOrigin;
|
|
116
|
+
try {
|
|
117
|
+
newOrigin = safeOrigin ? new URL(safeOrigin).hostname : undefined;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
newOrigin = undefined;
|
|
121
|
+
}
|
|
122
|
+
// Validate siteInfo strictly so downstream sites must provide the contract
|
|
123
|
+
assertSiteInfo(siteInfo);
|
|
110
124
|
// Use props if provided, otherwise fall back to siteInfo
|
|
111
125
|
const site_name = prop_site_name || siteInfo?.name;
|
|
112
126
|
const email = prop_email || siteInfo?.email;
|
|
@@ -114,5 +128,5 @@ export function generateMetaTags(props) {
|
|
|
114
128
|
const image_height = prop_image_height || siteInfo?.image_height;
|
|
115
129
|
const image_width = prop_image_width || siteInfo?.image_width;
|
|
116
130
|
const favicon = prop_favicon || siteInfo?.favicon;
|
|
117
|
-
return (_jsxs(_Fragment, { children: [_jsx("title", { children: title }), _jsx("meta", { charSet: "UTF-8" }), _jsx("meta", { httpEquiv: "content-type", content: "text/html; charset=UTF-8" }), _jsx("meta", { httpEquiv: 'Expires', content: '0' }), _jsx("meta", { httpEquiv: 'Pragma', content: 'no-cache' }), _jsx("meta", { httpEquiv: 'Cache-Control', content: 'no-cache' }), _jsx("meta", { name: "application-name", content: site_name }), _jsx("meta", { name: "author", content: site_name + ", " + email }), _jsx("meta", { name: 'copyright', content: site_name }), _jsx("meta", { name: "creator", content: site_name }), _jsx("meta", { name: "description", content: description }), _jsx("meta", { name: "keywords", content: keywords }), _jsx("meta", { name: 'language', content: 'EN' }), _jsx("meta", { name: 'owner', content: site_name }), _jsx("meta", { name: "publisher", content: site_name }), _jsx("meta", { name: 'rating', content: 'General' }), _jsx("meta", { name: 'reply-to', content: email }), _jsx("meta", { name: "robots", content: "index, follow" }), _jsx("meta", { name: 'url', content: url }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0, shrink-to-fit=no" }), _jsx("meta", { property: "og:description", content: description }), _jsx("meta", { property: 'og:email', content: email }), _jsx("meta", { property: "og:image", content: image }), _jsx("meta", { property: "og:image:height", content: image_height }), _jsx("meta", { property: "og:image:width", content: image_width }), _jsx("meta", { property: "og:locale", content: "en_US" }), _jsx("meta", { property: "og:site_name", content: site_name }), _jsx("meta", { property: "og:title", content: title }), _jsx("meta", { property: "og:type", content: "website" }), _jsx("meta", { property: "og:url", content: url }), _jsx("meta", { itemProp: "name", content: site_name }), _jsx("meta", { itemProp: "url", content: url }), _jsx("meta", { itemProp: "description", content: description }), _jsx("meta", { itemProp: "thumbnailUrl", content: image }), _jsx("meta", { property: "twitter:domain", content:
|
|
131
|
+
return (_jsxs(_Fragment, { children: [_jsx("title", { children: title }), _jsx("meta", { charSet: "UTF-8" }), _jsx("meta", { httpEquiv: "content-type", content: "text/html; charset=UTF-8" }), _jsx("meta", { httpEquiv: 'Expires', content: '0' }), _jsx("meta", { httpEquiv: 'Pragma', content: 'no-cache' }), _jsx("meta", { httpEquiv: 'Cache-Control', content: 'no-cache' }), _jsx("meta", { name: "application-name", content: site_name }), _jsx("meta", { name: "author", content: site_name + ", " + email }), _jsx("meta", { name: 'copyright', content: site_name }), _jsx("meta", { name: "creator", content: site_name }), _jsx("meta", { name: "description", content: description }), _jsx("meta", { name: "keywords", content: keywords }), _jsx("meta", { name: 'language', content: 'EN' }), _jsx("meta", { name: 'owner', content: site_name }), _jsx("meta", { name: "publisher", content: site_name }), _jsx("meta", { name: 'rating', content: 'General' }), _jsx("meta", { name: 'reply-to', content: email }), _jsx("meta", { name: "robots", content: "index, follow" }), _jsx("meta", { name: 'url', content: url }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0, shrink-to-fit=no" }), _jsx("meta", { property: "og:description", content: description }), _jsx("meta", { property: 'og:email', content: email }), _jsx("meta", { property: "og:image", content: image }), _jsx("meta", { property: "og:image:height", content: image_height != null ? String(image_height) : undefined }), _jsx("meta", { property: "og:image:width", content: image_width != null ? String(image_width) : undefined }), _jsx("meta", { property: "og:locale", content: "en_US" }), _jsx("meta", { property: "og:site_name", content: site_name }), _jsx("meta", { property: "og:title", content: title }), _jsx("meta", { property: "og:type", content: "website" }), _jsx("meta", { property: "og:url", content: url }), _jsx("meta", { itemProp: "name", content: site_name }), _jsx("meta", { itemProp: "url", content: url }), _jsx("meta", { itemProp: "description", content: description }), _jsx("meta", { itemProp: "thumbnailUrl", content: image }), _jsx("meta", { property: "twitter:domain", content: newOrigin }), _jsx("meta", { property: "twitter:url", content: url }), _jsx("meta", { name: "twitter:card", content: "summary_large_image" }), _jsx("meta", { name: "twitter:creator", content: site_name }), _jsx("meta", { name: "twitter:description", content: description }), _jsx("meta", { name: "twitter:image", content: image }), _jsx("meta", { name: "twitter:image:height", content: image_height != null ? String(image_height) : undefined }), _jsx("meta", { name: "twitter:image:width", content: image_width != null ? String(image_width) : undefined }), _jsx("meta", { name: "twitter:title", content: title }), _jsx("link", { rel: "author", href: newOrigin }), _jsx("link", { rel: "canonical", href: url }), _jsx("link", { rel: "icon", type: "image/x-icon", href: favicon }), _jsx("link", { rel: "shortcut icon", type: "image/x-icon", href: favicon }), _jsx("link", { rel: "manifest", href: "/manifest.webmanifest" }), _jsx("link", { rel: "preconnect", href: "https://images.ctfassets.net/" }), _jsx("link", { rel: "preconnect", href: "https://res.cloudinary.com/" }), _jsx("link", { rel: "preconnect", href: "https://farm2.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm6.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm8.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm66.static.flickr.com" })] }));
|
|
118
132
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
const debug = false;
|
|
3
|
+
export function attachProxyCspListener(onCsp) {
|
|
4
|
+
const handler = (e) => {
|
|
5
|
+
const info = {
|
|
6
|
+
blockedURI: e.blockedURI,
|
|
7
|
+
violatedDirective: e.violatedDirective,
|
|
8
|
+
originalPolicy: e.originalPolicy,
|
|
9
|
+
sourceFile: e?.sourceFile,
|
|
10
|
+
disposition: e.disposition,
|
|
11
|
+
referrer: e.referrer,
|
|
12
|
+
};
|
|
13
|
+
const defaultHandler = (i) => { if (debug)
|
|
14
|
+
console.warn('CSP violation', i); };
|
|
15
|
+
(onCsp ?? defaultHandler)(info);
|
|
16
|
+
};
|
|
17
|
+
window.addEventListener('securitypolicyviolation', handler);
|
|
18
|
+
// Return a cleanup function so callers can detach the listener
|
|
19
|
+
return () => window.removeEventListener('securitypolicyviolation', handler);
|
|
20
|
+
}
|
|
@@ -41,10 +41,12 @@ export function handlePixelatedProxy(req) {
|
|
|
41
41
|
response.headers.set("Permissions-Policy", "camera=(), microphone=(), geolocation=(), interest-cohort=()");
|
|
42
42
|
// Content Security Policy (CSP)
|
|
43
43
|
// Includes all discovered domains in the workspace: HubSpot, Gravatar, Flickr, Contentful, Cloudinary, eBay, and Google Analytics + Search.
|
|
44
|
+
const scriptSrc = "'self' 'unsafe-inline' 'unsafe-eval' https://va.vercel-scripts.com https://*.googletagmanager.com https://*.hs-scripts.com https://*.hs-analytics.net https://*.hsforms.net https://*.hscollectedforms.net https://*.hs-banner.com https://*.google.com https://*.doubleclick.net https://*.googleadservices.com https://*.adtrafficquality.google https://*.hsappstatic.net https://assets.calendly.com https://cdn.jsdelivr.net";
|
|
44
45
|
const csp = [
|
|
45
46
|
"default-src 'self'",
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
`script-src ${scriptSrc}`,
|
|
48
|
+
`script-src-elem ${scriptSrc}`,
|
|
49
|
+
"connect-src 'self' https: https://*.hubspot.com https://proxy.pixelated.tech https://sendmail.pixelated.tech https://*.google-analytics.com https://*.analytics.google.com https://cdn.jsdelivr.net",
|
|
48
50
|
"img-src 'self' data: https: https://*.gravatar.com https://*.staticflickr.com https://*.ctfassets.net https://res.cloudinary.com https://*.ebayimg.com",
|
|
49
51
|
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://*.google.com",
|
|
50
52
|
"font-src 'self' data: https://fonts.gstatic.com",
|
|
@@ -296,25 +296,18 @@ export async function createContentfulImageURLs(props) {
|
|
|
296
296
|
}
|
|
297
297
|
return sitemap;
|
|
298
298
|
}
|
|
299
|
-
const defaultEbayProps = {
|
|
300
|
-
proxyURL: "https://proxy.pixelated.tech/prod/proxy?url=",
|
|
301
|
-
baseTokenURL: 'https://api.ebay.com/identity/v1/oauth2/token',
|
|
302
|
-
tokenScope: 'https://api.ebay.com/oauth/api_scope',
|
|
303
|
-
baseSearchURL: 'https://api.ebay.com/buy/browse/v1/item_summary/search',
|
|
304
|
-
qsSearchURL: '?q=sunglasses&fieldgroups=full&category_ids=79720&aspect_filter=categoryId:79720&filter=sellers:{pixelatedtech}&sort=newlyListed&limit=200',
|
|
305
|
-
baseItemURL: 'https://api.ebay.com/buy/browse/v1/item',
|
|
306
|
-
qsItemURL: '/v1|295959752403|0?fieldgroups=PRODUCT,ADDITIONAL_SELLER_DETAILS',
|
|
307
|
-
appId: 'BrianWha-Pixelate-PRD-1fb4458de-1a8431fe', // clientId
|
|
308
|
-
appCertId: 'PRD-fb4458deef01-0d54-496a-b572-a04b', // clientSecret
|
|
309
|
-
sbxAppId: 'BrianWha-Pixelate-SBX-ad482b6ae-8cb8fead', // Sandbox
|
|
310
|
-
sbxAppCertId: '',
|
|
311
|
-
globalId: 'EBAY-US',
|
|
312
|
-
};
|
|
313
299
|
export async function createEbayItemURLs(origin) {
|
|
314
300
|
const sitemap = [];
|
|
315
|
-
|
|
301
|
+
// Load configuration
|
|
302
|
+
const config = getFullPixelatedConfig();
|
|
303
|
+
const globalProxy = config.global?.proxyUrl;
|
|
304
|
+
const ebayProps = {
|
|
305
|
+
...(globalProxy ? { proxyURL: globalProxy } : {}),
|
|
306
|
+
...config.ebay
|
|
307
|
+
};
|
|
308
|
+
await getEbayAppToken({ apiProps: ebayProps })
|
|
316
309
|
.then(async (response) => {
|
|
317
|
-
await getEbayItemsSearch({ apiProps:
|
|
310
|
+
await getEbayItemsSearch({ apiProps: ebayProps, token: response })
|
|
318
311
|
.then((items) => {
|
|
319
312
|
for (const item of items.itemSummaries) {
|
|
320
313
|
sitemap.push({
|
|
@@ -4,11 +4,11 @@ import { useState, useEffect } from "react";
|
|
|
4
4
|
import PropTypes from "prop-types";
|
|
5
5
|
import { Carousel } from '../general/carousel';
|
|
6
6
|
import { SmartImage } from "../general/smartimage";
|
|
7
|
-
import {
|
|
7
|
+
import { getEbayItems, getEbayItem, getShoppingCartItem, getEbayRateLimits, getEbayAppToken } from "./ebay.functions";
|
|
8
8
|
import { addToShoppingCart } from "./shoppingcart.functions";
|
|
9
9
|
import { AddToCartButton, /* GoToCartButton */ ViewItemDetails } from "./shoppingcart.components";
|
|
10
10
|
import { getCloudinaryRemoteFetchURL as getImg } from "../general/cloudinary";
|
|
11
|
-
import {
|
|
11
|
+
import { ToggleLoading } from "../general/loading";
|
|
12
12
|
import { usePixelatedConfig } from "../config/config.client";
|
|
13
13
|
import "../../css/pixelated.grid.scss";
|
|
14
14
|
import "./ebay.css";
|
|
@@ -20,9 +20,10 @@ EbayItems.propTypes = {
|
|
|
20
20
|
};
|
|
21
21
|
export function EbayItems(props) {
|
|
22
22
|
// https://developer.ebay.com/devzone/finding/HowTo/GettingStarted_JS_NV_JSON/GettingStarted_JS_NV_JSON.html
|
|
23
|
+
const config = usePixelatedConfig();
|
|
23
24
|
const [items, setItems] = useState([]);
|
|
24
25
|
const [aspects, setAspects] = useState([]);
|
|
25
|
-
const
|
|
26
|
+
const apiProps = { ...(config?.ebay || {}), ...props.apiProps };
|
|
26
27
|
paintItems.propTypes = {
|
|
27
28
|
items: PropTypes.array.isRequired,
|
|
28
29
|
cloudinaryProductEnv: PropTypes.string,
|
|
@@ -33,7 +34,7 @@ export function EbayItems(props) {
|
|
|
33
34
|
let newItems = [];
|
|
34
35
|
for (let key in props.items) {
|
|
35
36
|
const item = props.items[key];
|
|
36
|
-
const newItem = _jsx(EbayListItem, { item: item, cloudinaryProductEnv: props.cloudinaryProductEnv }, item.legacyItemId);
|
|
37
|
+
const newItem = _jsx(EbayListItem, { item: item, apiProps: apiProps, cloudinaryProductEnv: props.cloudinaryProductEnv }, item.legacyItemId);
|
|
37
38
|
newItems.push(newItem);
|
|
38
39
|
}
|
|
39
40
|
return newItems;
|
|
@@ -44,6 +45,8 @@ export function EbayItems(props) {
|
|
|
44
45
|
};
|
|
45
46
|
async function fetchItems(props) {
|
|
46
47
|
try {
|
|
48
|
+
if (debug)
|
|
49
|
+
console.log("Fetching ebay API Items Data");
|
|
47
50
|
const myApiProps = { ...apiProps };
|
|
48
51
|
if (props) {
|
|
49
52
|
const params = new URLSearchParams(myApiProps.qsSearchURL);
|
|
@@ -55,9 +58,9 @@ export function EbayItems(props) {
|
|
|
55
58
|
}
|
|
56
59
|
const response = await getEbayItems({ apiProps: myApiProps });
|
|
57
60
|
if (debug)
|
|
58
|
-
console.log("eBay API
|
|
59
|
-
setItems(response?.itemSummaries
|
|
60
|
-
setAspects(response?.refinement?.aspectDistributions
|
|
61
|
+
console.log("eBay API Search Items Data:", response);
|
|
62
|
+
setItems(response?.itemSummaries);
|
|
63
|
+
setAspects(response?.refinement?.aspectDistributions);
|
|
61
64
|
}
|
|
62
65
|
catch (error) {
|
|
63
66
|
console.error("Error fetching eBay items:", error);
|
|
@@ -70,11 +73,11 @@ export function EbayItems(props) {
|
|
|
70
73
|
fetchItems();
|
|
71
74
|
ToggleLoading(false);
|
|
72
75
|
}, []);
|
|
73
|
-
if (items.length > 0) {
|
|
74
|
-
return (_jsxs(_Fragment, { children: [_jsx(
|
|
76
|
+
if (items && items.length > 0) {
|
|
77
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "ebayItemsHeader", children: _jsx(EbayItemHeader, { title: `${items.length} Store Items` }) }), _jsx("div", { className: "ebayItemsHeader", children: _jsx(EbayListFilter, { aspects: aspects, callback: fetchItems }) }), _jsx("div", { id: "ebayItems", className: "ebayItems", children: paintItems({ items: items, cloudinaryProductEnv: props.cloudinaryProductEnv }) })] }));
|
|
75
78
|
}
|
|
76
79
|
else {
|
|
77
|
-
return (_jsx("div", { className: "section-container", children: _jsx("div", { id: "ebayItems", className: "ebayItems"
|
|
80
|
+
return (_jsx("div", { className: "section-container", children: _jsx("div", { id: "ebayItems", className: "ebayItems" }) }));
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
EbayListFilter.propTypes = {
|
|
@@ -124,16 +127,18 @@ export function EbayListFilter(props) {
|
|
|
124
127
|
EbayListItem.propTypes = {
|
|
125
128
|
item: PropTypes.any.isRequired,
|
|
126
129
|
cloudinaryProductEnv: PropTypes.string,
|
|
130
|
+
apiProps: PropTypes.any,
|
|
127
131
|
};
|
|
128
132
|
export function EbayListItem(props) {
|
|
129
133
|
const thisItem = props.item;
|
|
134
|
+
const apiProps = props.apiProps;
|
|
130
135
|
// const itemURL = thisItem.itemWebUrl;
|
|
131
136
|
const itemURL = "./store/" + thisItem.legacyItemId;
|
|
132
137
|
const itemURLTarget = "_self"; /* "_blank" */
|
|
133
138
|
const itemImage = (props.cloudinaryProductEnv)
|
|
134
139
|
? getImg({ url: thisItem.thumbnailImages[0].imageUrl, product_env: props.cloudinaryProductEnv })
|
|
135
140
|
: thisItem.thumbnailImages[0].imageUrl;
|
|
136
|
-
const shoppingCartItem = getShoppingCartItem({ thisItem: thisItem, cloudinaryProductEnv: props.cloudinaryProductEnv });
|
|
141
|
+
const shoppingCartItem = getShoppingCartItem({ thisItem: thisItem, cloudinaryProductEnv: props.cloudinaryProductEnv, apiProps: apiProps });
|
|
137
142
|
// CHANGE EBAY URL TO LOCAL EBAY ITEM DETAIL URL
|
|
138
143
|
shoppingCartItem.itemURL = itemURL;
|
|
139
144
|
const config = usePixelatedConfig();
|
|
@@ -143,7 +148,7 @@ export function EbayListItem(props) {
|
|
|
143
148
|
? _jsx("a", { href: itemURL, target: itemURLTarget, rel: "noopener noreferrer", children: itemImageComponent })
|
|
144
149
|
: (itemImageComponent) }), _jsxs("div", { className: "ebayItemBody grid-s5-e13", children: [_jsx("div", { className: "ebayItemHeader", children: itemURL
|
|
145
150
|
? _jsx(EbayItemHeader, { url: itemURL, target: itemURLTarget, title: thisItem.title })
|
|
146
|
-
: _jsx(EbayItemHeader, { title: thisItem.title }) }), _jsxs("div", { className: "ebayItemDetails grid12", children: [_jsxs("div", { children: [_jsx("b", { children: "Item ID: " }), thisItem.legacyItemId] }), _jsxs("div", { children: [_jsx("b", { children: "Quantity: " }), thisItem.categories[0].categoryId ==
|
|
151
|
+
: _jsx(EbayItemHeader, { title: thisItem.title }) }), _jsxs("div", { className: "ebayItemDetails grid12", children: [_jsxs("div", { children: [_jsx("b", { children: "Item ID: " }), thisItem.legacyItemId] }), _jsxs("div", { children: [_jsx("b", { children: "Quantity: " }), thisItem.categories[0].categoryId == apiProps.itemCategory ? 1 : 10] }), _jsxs("div", { children: [_jsx("b", { children: "Condition: " }), thisItem.condition] }), _jsxs("div", { children: [_jsx("b", { children: "Seller: " }), thisItem.seller.username, " (", thisItem.seller.feedbackScore, ")", _jsx("br", {}), thisItem.seller.feedbackPercentage, "% positive"] }), _jsxs("div", { children: [_jsx("b", { children: "Buying Options: " }), thisItem.buyingOptions[0]] }), _jsxs("div", { children: [_jsx("b", { children: "Location: " }), thisItem.itemLocation.postalCode + ", " + thisItem.itemLocation.country] }), _jsxs("div", { children: [_jsx("b", { children: "Listing Date: " }), thisItem.itemCreationDate] })] }), _jsx("div", { className: "ebayItemPrice", children: itemURL
|
|
147
152
|
? _jsxs("a", { href: itemURL, target: itemURLTarget, rel: "noreferrer", children: ["$", thisItem.price.value + " " + thisItem.price.currency] })
|
|
148
153
|
: "$" + thisItem.price.value + " " + thisItem.price.currency }), _jsx("br", {}), _jsxs("div", { className: "ebayItemAddToCart", children: [_jsx(ViewItemDetails, { href: "/store", itemID: thisItem.legacyItemId }), _jsx(AddToCartButton, { handler: addToShoppingCart, item: shoppingCartItem, itemID: thisItem.legacyItemId })] })] })] }));
|
|
149
154
|
}
|
|
@@ -164,8 +169,9 @@ EbayItemDetail.propTypes = {
|
|
|
164
169
|
cloudinaryProductEnv: PropTypes.string,
|
|
165
170
|
};
|
|
166
171
|
export function EbayItemDetail(props) {
|
|
172
|
+
const config = usePixelatedConfig();
|
|
167
173
|
const [item, setItem] = useState({});
|
|
168
|
-
const
|
|
174
|
+
const apiProps = { ...(config?.ebay || {}), ...props.apiProps };
|
|
169
175
|
useEffect(() => {
|
|
170
176
|
if (debug)
|
|
171
177
|
console.log("Running useEffect");
|
|
@@ -191,11 +197,11 @@ export function EbayItemDetail(props) {
|
|
|
191
197
|
: thisImage.imageUrl }));
|
|
192
198
|
const itemURL = undefined;
|
|
193
199
|
const itemURLTarget = "_self"; /* "_blank" */
|
|
194
|
-
const shoppingCartItem = getShoppingCartItem({ thisItem: thisItem, cloudinaryProductEnv: props.cloudinaryProductEnv });
|
|
200
|
+
const shoppingCartItem = getShoppingCartItem({ thisItem: thisItem, cloudinaryProductEnv: props.cloudinaryProductEnv, apiProps: apiProps });
|
|
195
201
|
shoppingCartItem.itemURL = itemURL;
|
|
196
202
|
return (_jsx(_Fragment, { children: _jsxs("div", { className: "ebayItem row-12col", children: [_jsx("div", { className: "ebayItemHeader grid-s1-e13", children: itemURL
|
|
197
203
|
? _jsx(EbayItemHeader, { url: itemURL, title: thisItem.title })
|
|
198
|
-
: _jsx(EbayItemHeader, { title: thisItem.title }) }), _jsx("br", {}), _jsx("div", { className: "ebayItemPhotoCarousel grid-s1-e7", children: _jsx(Carousel, { cards: images, draggable: true, imgFit: "contain" }) }), _jsxs("div", { className: "grid-s7-e13", children: [_jsx("div", { className: "ebayItemDetails grid12", children: _jsx("div", { dangerouslySetInnerHTML: { __html: thisItem.description.replace(/(<br\s*\/?>\s*){2,}/gi, '') } }) }), _jsx("br", {}), _jsxs("div", { className: "ebayItemDetails grid12", children: [_jsxs("div", { children: [_jsx("b", { children: "Item ID: " }), thisItem.legacyItemId] }), _jsxs("div", { children: [_jsx("b", { children: "Quantity: " }), thisItem.categoryId ==
|
|
204
|
+
: _jsx(EbayItemHeader, { title: thisItem.title }) }), _jsx("br", {}), _jsx("div", { className: "ebayItemPhotoCarousel grid-s1-e7", children: _jsx(Carousel, { cards: images, draggable: true, imgFit: "contain" }) }), _jsxs("div", { className: "grid-s7-e13", children: [_jsx("div", { className: "ebayItemDetails grid12", children: _jsx("div", { dangerouslySetInnerHTML: { __html: thisItem.description.replace(/(<br\s*\/?>\s*){2,}/gi, '') } }) }), _jsx("br", {}), _jsxs("div", { className: "ebayItemDetails grid12", children: [_jsxs("div", { children: [_jsx("b", { children: "Item ID: " }), thisItem.legacyItemId] }), _jsxs("div", { children: [_jsx("b", { children: "Quantity: " }), thisItem.categoryId == apiProps.itemCategory ? 1 : 10] }), _jsxs("div", { children: [_jsx("b", { children: "Category: " }), thisItem.categoryPath] }), _jsxs("div", { children: [_jsx("b", { children: "Condition: " }), thisItem.condition] }), _jsxs("div", { children: [_jsx("b", { children: "Seller: " }), thisItem.seller.username, " (", thisItem.seller.feedbackScore, ")", _jsx("br", {}), thisItem.seller.feedbackPercentage, "% positive"] }), _jsxs("div", { children: [_jsx("b", { children: "Buying Options: " }), thisItem.buyingOptions[0]] }), _jsxs("div", { children: [_jsx("b", { children: "Location: " }), thisItem.itemLocation.city + ", " + thisItem.itemLocation.stateOrProvince] }), _jsxs("div", { children: [_jsx("b", { children: "Listing Date: " }), thisItem.itemCreationDate] }), _jsx("br", {})] }), _jsx("div", { className: "ebayItemPrice", children: itemURL
|
|
199
205
|
? _jsxs("a", { href: itemURL, target: itemURLTarget, rel: "noreferrer", children: ["$", thisItem.price.value + " " + thisItem.price.currency] })
|
|
200
206
|
: "$" + thisItem.price.value + " " + thisItem.price.currency }), _jsx("br", {}), _jsx("div", { className: "ebayItemAddToCart", children: _jsx(AddToCartButton, { handler: addToShoppingCart, item: shoppingCartItem, itemID: thisItem.legacyItemId }) })] })] }) }));
|
|
201
207
|
}
|
|
@@ -203,3 +209,105 @@ export function EbayItemDetail(props) {
|
|
|
203
209
|
return (_jsx(_Fragment, { children: _jsx("div", { id: "ebayItems", className: "ebayItems", children: _jsx("div", { className: "centered", children: "Loading..." }) }) }));
|
|
204
210
|
}
|
|
205
211
|
}
|
|
212
|
+
/* ========== EBAY RATE LIMITS VISUALIZER ========== */
|
|
213
|
+
EbayRateLimitsVisualizer.propTypes = {
|
|
214
|
+
token: PropTypes.string,
|
|
215
|
+
apiProps: PropTypes.object,
|
|
216
|
+
};
|
|
217
|
+
export function EbayRateLimitsVisualizer(props) {
|
|
218
|
+
const config = usePixelatedConfig();
|
|
219
|
+
const apiProps = {
|
|
220
|
+
...(config?.ebay || {}),
|
|
221
|
+
...props.apiProps
|
|
222
|
+
};
|
|
223
|
+
const [token, setToken] = useState(props.token || '');
|
|
224
|
+
const [data, setData] = useState(null);
|
|
225
|
+
const [loading, setLoading] = useState(false);
|
|
226
|
+
const [fetchingToken, setFetchingToken] = useState(false);
|
|
227
|
+
const [error, setError] = useState(null);
|
|
228
|
+
const fetchToken = async () => {
|
|
229
|
+
setFetchingToken(true);
|
|
230
|
+
setError(null);
|
|
231
|
+
try {
|
|
232
|
+
const newToken = await getEbayAppToken({ apiProps });
|
|
233
|
+
if (newToken) {
|
|
234
|
+
setToken(newToken);
|
|
235
|
+
return newToken;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
setError('Failed to fetch eBay token. Check your appId and appCertId.');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch (e) {
|
|
242
|
+
setError('Token fetch error: ' + e.message);
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
setFetchingToken(false);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
// Auto-fetch token on mount if credentials are available
|
|
249
|
+
useEffect(() => {
|
|
250
|
+
if (!token && apiProps.appId && apiProps.appCertId) {
|
|
251
|
+
fetchToken();
|
|
252
|
+
}
|
|
253
|
+
}, []);
|
|
254
|
+
const fetchData = async () => {
|
|
255
|
+
setLoading(true);
|
|
256
|
+
setError(null);
|
|
257
|
+
try {
|
|
258
|
+
const result = await getEbayRateLimits({
|
|
259
|
+
token: token,
|
|
260
|
+
apiProps: apiProps
|
|
261
|
+
});
|
|
262
|
+
setData(result);
|
|
263
|
+
}
|
|
264
|
+
catch (e) {
|
|
265
|
+
setError(e.message || 'Failed to fetch data');
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
setLoading(false);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
const showMockData = () => {
|
|
272
|
+
setData({
|
|
273
|
+
rate_limit: {
|
|
274
|
+
apiContext: "Buy",
|
|
275
|
+
apiName: "Browse",
|
|
276
|
+
apiVersion: "v1",
|
|
277
|
+
resources: [
|
|
278
|
+
{
|
|
279
|
+
resourceName: "item_summary",
|
|
280
|
+
methods: [
|
|
281
|
+
{
|
|
282
|
+
methodName: "search",
|
|
283
|
+
quotaTotal: 5000,
|
|
284
|
+
quotaRemaining: 4950,
|
|
285
|
+
quotaResets: "2026-01-10T00:00:00.000Z"
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
}
|
|
289
|
+
]
|
|
290
|
+
},
|
|
291
|
+
user_rate_limit: {
|
|
292
|
+
userContext: "Individual",
|
|
293
|
+
resources: [
|
|
294
|
+
{
|
|
295
|
+
resourceName: "item",
|
|
296
|
+
methods: [
|
|
297
|
+
{
|
|
298
|
+
methodName: "get",
|
|
299
|
+
quotaTotal: 1000,
|
|
300
|
+
quotaRemaining: 990,
|
|
301
|
+
quotaResets: "2026-01-10T00:00:00.000Z"
|
|
302
|
+
}
|
|
303
|
+
]
|
|
304
|
+
}
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
// Check for API-level errors in the returned data
|
|
310
|
+
const hasTokenError = data?.rate_limit?.errors?.[0]?.message === "Invalid access token" ||
|
|
311
|
+
data?.user_rate_limit?.errors?.[0]?.message === "Invalid access token";
|
|
312
|
+
return (_jsxs("div", { style: { padding: '20px', fontFamily: 'sans-serif', border: '1px solid #ddd', borderRadius: '8px' }, children: [_jsx("h3", { children: "Ebay Rate Limits Data Visualizer" }), _jsxs("div", { style: { marginBottom: '20px', display: 'flex', flexDirection: 'column', gap: '10px' }, children: [_jsxs("div", { style: { display: 'flex', gap: '10px', alignItems: 'center' }, children: [_jsx("label", { htmlFor: "ebay-token", children: _jsx("b", { children: "Token:" }) }), _jsx("input", { id: "ebay-token", type: "text", value: token, onChange: (e) => setToken(e.target.value), placeholder: "Paste eBay token here", style: { flexGrow: 1, padding: '5px', fontFamily: 'monospace' } }), _jsx("button", { onClick: fetchToken, disabled: fetchingToken || !apiProps.appId, children: fetchingToken ? 'Fetching Token...' : 'Auto-Fetch Token' })] }), _jsxs("div", { style: { display: 'flex', gap: '10px' }, children: [_jsx("button", { onClick: fetchData, disabled: loading || !token, style: { padding: '8px 16px', cursor: 'pointer', background: '#0070f3', color: 'white', border: 'none', borderRadius: '4px' }, children: loading ? 'Fetching Limits...' : 'Fetch Rate Limits' }), _jsx("button", { onClick: showMockData, style: { padding: '8px 16px', cursor: 'pointer', border: '1px solid #ccc', borderRadius: '4px', background: 'white' }, children: "Load Sample Structure" })] })] }), error && (_jsxs("div", { style: { color: '#d00', background: '#fff5f5', padding: '10px', borderRadius: '4px', marginBottom: '10px', border: '1px solid #feb2b2' }, children: [_jsx("b", { children: "Error:" }), " ", error] })), hasTokenError && (_jsxs("div", { style: { color: '#c53030', background: '#fff5f5', padding: '10px', borderRadius: '4px', marginBottom: '10px', border: '1px solid #feb2b2' }, children: ["\uD83D\uDEA8 ", _jsx("b", { children: "Authentication Error:" }), " Your eBay access token is invalid or expired. Use \"Auto-Fetch Token\" if credentials are set, or paste a new one."] })), data ? (_jsxs("div", { style: { marginTop: '20px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [_jsx("h4", { children: "Response Data:" }), _jsx("button", { onClick: () => setData(null), style: { padding: '4px 8px', fontSize: '12px' }, children: "Clear" })] }), _jsx("pre", { style: { background: '#f8f9fa', padding: '15px', borderRadius: '5px', overflow: 'auto', border: '1px solid #e9ecef', fontSize: '13px' }, children: JSON.stringify(data, null, 2) })] })) : (_jsx("div", { style: { color: '#666', fontStyle: 'italic', marginTop: '20px' }, children: "No rate limit data loaded yet." }))] }));
|
|
313
|
+
}
|