@pixelated-tech/components 3.14.5 → 3.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/admin/site-health/site-health-core-web-vitals.integration.js +21 -8
- package/dist/components/admin/site-health/site-health-github.integration.js +6 -6
- package/dist/components/admin/site-health/site-health-on-site-seo.integration.js +36 -16
- package/dist/components/admin/site-health/site-health-template.js +10 -6
- package/dist/components/config/config.types.js +12 -0
- package/dist/components/general/markdown.js +35 -0
- package/dist/components/general/nerdjoke.js +2 -4
- package/dist/components/general/proxy-handler.js +2 -2
- package/dist/components/general/sitemap.js +2 -4
- package/dist/components/general/smartfetch.js +211 -0
- package/dist/components/general/tiles.js +1 -1
- package/dist/components/general/urlbuilder.js +74 -0
- package/dist/components/integrations/contentful.delivery.js +24 -20
- package/dist/components/integrations/contentful.items.components.js +6 -2
- package/dist/components/integrations/contentful.management.js +188 -151
- package/dist/components/integrations/flickr.js +15 -22
- package/dist/components/integrations/gemini-api.client.js +22 -21
- package/dist/components/integrations/gemini-api.server.js +50 -46
- package/dist/components/integrations/google.reviews.functions.js +19 -5
- package/dist/components/integrations/googleplaces.js +33 -9
- package/dist/components/integrations/gravatar.functions.js +15 -7
- package/dist/components/integrations/hubspot.components.js +8 -10
- package/dist/components/integrations/instagram.functions.js +9 -4
- package/dist/components/integrations/lipsum.js +6 -10
- package/dist/components/integrations/loremipsum.js +21 -21
- package/dist/components/integrations/socialcard.js +14 -8
- package/dist/components/integrations/spotify.functions.js +7 -4
- package/dist/components/integrations/wordpress.functions.js +17 -19
- package/dist/components/integrations/yelp.js +6 -7
- package/dist/components/shoppingcart/ebay.functions.js +69 -53
- package/dist/components/shoppingcart/shoppingcart.components.js +1 -1
- package/dist/components/sitebuilder/config/google-fonts.js +13 -6
- package/dist/components/sitebuilder/form/formbuilder.js +1 -1
- package/dist/components/sitebuilder/form/formengine.js +37 -10
- package/dist/components/sitebuilder/form/formsubmit.js +205 -0
- package/dist/components/sitebuilder/page/components/SaveLoadSection.js +24 -12
- package/dist/config/pixelated.config.json.enc +1 -1
- package/dist/data/form.json +7 -0
- package/dist/index.js +4 -2
- package/dist/index.server.js +3 -1
- package/dist/scripts/pixelated-eslint-plugin.js +51 -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-github.integration.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-on-site-seo.integration.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-template.d.ts.map +1 -1
- package/dist/types/components/config/config.types.d.ts +11 -0
- package/dist/types/components/config/config.types.d.ts.map +1 -1
- package/dist/types/components/general/markdown.d.ts +12 -0
- package/dist/types/components/general/markdown.d.ts.map +1 -1
- package/dist/types/components/general/nerdjoke.d.ts.map +1 -1
- package/dist/types/components/general/proxy-handler.d.ts.map +1 -1
- package/dist/types/components/general/sitemap.d.ts.map +1 -1
- package/dist/types/components/general/smartfetch.d.ts +85 -0
- package/dist/types/components/general/smartfetch.d.ts.map +1 -0
- package/dist/types/components/general/tiles.d.ts.map +1 -1
- package/dist/types/components/general/urlbuilder.d.ts +64 -0
- package/dist/types/components/general/urlbuilder.d.ts.map +1 -0
- package/dist/types/components/integrations/contentful.delivery.d.ts.map +1 -1
- package/dist/types/components/integrations/contentful.items.components.d.ts.map +1 -1
- package/dist/types/components/integrations/contentful.management.d.ts.map +1 -1
- package/dist/types/components/integrations/flickr.d.ts.map +1 -1
- package/dist/types/components/integrations/gemini-api.client.d.ts.map +1 -1
- package/dist/types/components/integrations/gemini-api.server.d.ts +1 -1
- package/dist/types/components/integrations/gemini-api.server.d.ts.map +1 -1
- package/dist/types/components/integrations/google.reviews.functions.d.ts.map +1 -1
- package/dist/types/components/integrations/googleplaces.d.ts.map +1 -1
- package/dist/types/components/integrations/gravatar.functions.d.ts.map +1 -1
- package/dist/types/components/integrations/hubspot.components.d.ts.map +1 -1
- package/dist/types/components/integrations/instagram.functions.d.ts.map +1 -1
- package/dist/types/components/integrations/lipsum.d.ts.map +1 -1
- package/dist/types/components/integrations/loremipsum.d.ts.map +1 -1
- package/dist/types/components/integrations/socialcard.d.ts.map +1 -1
- package/dist/types/components/integrations/spotify.functions.d.ts.map +1 -1
- package/dist/types/components/integrations/wordpress.functions.d.ts.map +1 -1
- package/dist/types/components/integrations/yelp.d.ts.map +1 -1
- package/dist/types/components/shoppingcart/ebay.functions.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/config/google-fonts.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/form/formengine.d.ts +4 -4
- package/dist/types/components/sitebuilder/form/formengine.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/form/{formutils.d.ts → formengineutilities.d.ts} +1 -1
- package/dist/types/components/sitebuilder/form/formengineutilities.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/form/formsubmit.d.ts +70 -0
- package/dist/types/components/sitebuilder/form/formsubmit.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/components/SaveLoadSection.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.server.d.ts +3 -1
- package/dist/types/scripts/pixelated-eslint-plugin.d.ts +21 -0
- package/dist/types/stories/admin/contentful-migration.stories.d.ts +43 -0
- package/dist/types/stories/admin/contentful-migration.stories.d.ts.map +1 -1
- package/dist/types/stories/general/text-generation.stories.d.ts +116 -0
- package/dist/types/stories/general/text-generation.stories.d.ts.map +1 -0
- package/dist/types/stories/integrations/google.reviews.stories.d.ts +52 -0
- package/dist/types/stories/integrations/google.reviews.stories.d.ts.map +1 -1
- package/dist/types/stories/integrations/gravatar.stories.d.ts.map +1 -1
- package/dist/types/stories/integrations/instagram.stories.d.ts +38 -0
- package/dist/types/stories/integrations/instagram.stories.d.ts.map +1 -1
- package/dist/types/stories/sitebuilder/form-engine.stories.d.ts +13 -7
- package/dist/types/stories/sitebuilder/form-engine.stories.d.ts.map +1 -1
- package/dist/types/stories/sitebuilder/form.honeypot.stories.d.ts +0 -19
- package/dist/types/stories/sitebuilder/form.honeypot.stories.d.ts.map +1 -1
- package/dist/types/test/test-utils.d.ts +2 -0
- package/dist/types/test/test-utils.d.ts.map +1 -1
- package/dist/types/tests/formengineutilities.test.d.ts +2 -0
- package/dist/types/tests/formengineutilities.test.d.ts.map +1 -0
- package/dist/types/tests/google-apis.test.d.ts +2 -0
- package/dist/types/tests/google-apis.test.d.ts.map +1 -0
- package/dist/types/tests/google-fonts.test.d.ts +2 -0
- package/dist/types/tests/google-fonts.test.d.ts.map +1 -0
- package/dist/types/tests/site-health-core-web-vitals.test.d.ts +2 -0
- package/dist/types/tests/site-health-core-web-vitals.test.d.ts.map +1 -0
- package/dist/types/tests/smartfetch.test.d.ts +2 -0
- package/dist/types/tests/smartfetch.test.d.ts.map +1 -0
- package/dist/types/tests/social-media-apis.test.d.ts +7 -0
- package/dist/types/tests/social-media-apis.test.d.ts.map +1 -0
- package/dist/types/tests/specialized-apis.test.d.ts +7 -0
- package/dist/types/tests/specialized-apis.test.d.ts.map +1 -0
- package/dist/types/tests/urlbuilder.test.d.ts +2 -0
- package/dist/types/tests/urlbuilder.test.d.ts.map +1 -0
- package/dist/types/tests/useFormSubmit.test.d.ts +2 -0
- package/dist/types/tests/useFormSubmit.test.d.ts.map +1 -0
- package/package.json +9 -9
- package/dist/components/sitebuilder/form/formemailer.js +0 -119
- package/dist/types/components/sitebuilder/form/formemailer.d.ts +0 -3
- package/dist/types/components/sitebuilder/form/formemailer.d.ts.map +0 -1
- package/dist/types/components/sitebuilder/form/formutils.d.ts.map +0 -1
- package/dist/types/stories/integrations/lipsum.stories.d.ts +0 -38
- package/dist/types/stories/integrations/lipsum.stories.d.ts.map +0 -1
- package/dist/types/stories/integrations/loremipsum.stories.d.ts +0 -46
- package/dist/types/stories/integrations/loremipsum.stories.d.ts.map +0 -1
- package/dist/types/tests/formemailer.honeypot.test.d.ts +0 -2
- package/dist/types/tests/formemailer.honeypot.test.d.ts.map +0 -1
- /package/dist/components/sitebuilder/form/{formutils.js → formengineutilities.js} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import PropTypes from "prop-types";
|
|
2
|
+
import { smartFetch } from '../general/smartfetch';
|
|
3
|
+
import { buildUrl } from '../general/urlbuilder';
|
|
2
4
|
const debug = false;
|
|
3
5
|
const ctfQSParams = "?fm=webp&q=50";
|
|
4
6
|
/* ========== CALL CONTENTFUL DELIVERY API ========== */
|
|
@@ -15,11 +17,7 @@ export async function callContentfulDeliveryAPI(props) {
|
|
|
15
17
|
if (debug)
|
|
16
18
|
console.log("Calling Contentful Delivery API:", props.full_url);
|
|
17
19
|
try {
|
|
18
|
-
const
|
|
19
|
-
if (!response.ok) {
|
|
20
|
-
throw new Error(`Response status: ${response.status}`);
|
|
21
|
-
}
|
|
22
|
-
const json = await response.json();
|
|
20
|
+
const json = await smartFetch(props.full_url);
|
|
23
21
|
return json;
|
|
24
22
|
}
|
|
25
23
|
catch (error) {
|
|
@@ -60,11 +58,11 @@ getContentfulEntries.propTypes = {
|
|
|
60
58
|
};
|
|
61
59
|
export async function getContentfulEntries(props) {
|
|
62
60
|
const { base_url, space_id, environment, delivery_access_token } = props.apiProps;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
const full_url = buildUrl({
|
|
62
|
+
baseUrl: base_url,
|
|
63
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries'],
|
|
64
|
+
params: { access_token: delivery_access_token }
|
|
65
|
+
});
|
|
68
66
|
return await callContentfulDeliveryAPI({ full_url });
|
|
69
67
|
}
|
|
70
68
|
/*
|
|
@@ -124,11 +122,14 @@ getContentfulContentType.propTypes = {
|
|
|
124
122
|
};
|
|
125
123
|
export async function getContentfulContentType(props) {
|
|
126
124
|
const { base_url, space_id, environment, access_token } = props.apiProps;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
if (!base_url || !space_id || !environment) {
|
|
126
|
+
throw new Error('Contentful API properties not configured: base_url, space_id, or environment');
|
|
127
|
+
}
|
|
128
|
+
const full_url = buildUrl({
|
|
129
|
+
baseUrl: base_url,
|
|
130
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'content_types', props.contentType],
|
|
131
|
+
params: { access_token }
|
|
132
|
+
});
|
|
132
133
|
return await callContentfulDeliveryAPI({ full_url });
|
|
133
134
|
}
|
|
134
135
|
/* ========== GET CONTENTFUL ENTRY BY ENTRY ID ========== */
|
|
@@ -152,11 +153,14 @@ getContentfulEntryByEntryID.propTypes = {
|
|
|
152
153
|
};
|
|
153
154
|
export async function getContentfulEntryByEntryID(props) {
|
|
154
155
|
const { base_url, space_id, environment, delivery_access_token } = props.apiProps;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
if (!base_url || !space_id || !environment) {
|
|
157
|
+
throw new Error('Contentful API properties not configured: base_url, space_id, or environment');
|
|
158
|
+
}
|
|
159
|
+
const full_url = buildUrl({
|
|
160
|
+
baseUrl: base_url,
|
|
161
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries', props.entry_id],
|
|
162
|
+
params: { access_token: delivery_access_token }
|
|
163
|
+
});
|
|
160
164
|
return await callContentfulDeliveryAPI({ full_url });
|
|
161
165
|
}
|
|
162
166
|
/* ========== GET CONTENTFUL CARD BY FIELD ========== */
|
|
@@ -75,7 +75,11 @@ export function ContentfulItems(props) {
|
|
|
75
75
|
return a.sys.createdAt < b.sys.createdAt ? -1 : 1;
|
|
76
76
|
});
|
|
77
77
|
if (itemImagesMatches.length > 0) {
|
|
78
|
-
|
|
78
|
+
// Contentful Asset URLs start with two slashes, convert to absolute URL
|
|
79
|
+
const imageUrl = itemImagesMatches[0].fields.file.url;
|
|
80
|
+
item.fields.imageUrl = imageUrl.startsWith("//")
|
|
81
|
+
? "https:" + imageUrl
|
|
82
|
+
: imageUrl;
|
|
79
83
|
item.fields.imageAlt = itemImagesMatches[0].fields.title;
|
|
80
84
|
}
|
|
81
85
|
const newItem = _jsx(ContentfulListItem, { item: item, cloudinaryProductEnv: props.cloudinaryProductEnv }, item.sys.id);
|
|
@@ -141,7 +145,7 @@ export function ContentfulListItem(props) {
|
|
|
141
145
|
itemCost: thisItem.fields.price,
|
|
142
146
|
};
|
|
143
147
|
const itemImage = (props.cloudinaryProductEnv)
|
|
144
|
-
? getImg({ url:
|
|
148
|
+
? getImg({ url: thisItem.fields.imageUrl, product_env: props.cloudinaryProductEnv })
|
|
145
149
|
: thisItem.fields.imageUrl;
|
|
146
150
|
const config = usePixelatedConfig();
|
|
147
151
|
const imgComponent = _jsx(SmartImage, { src: itemImage, title: thisItem.fields.title, alt: thisItem.fields.title, cloudinaryEnv: config?.cloudinary?.product_env ?? undefined, cloudinaryDomain: config?.cloudinary?.baseUrl ?? undefined, cloudinaryTransforms: config?.cloudinary?.transforms ?? undefined });
|
|
@@ -3,22 +3,27 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides reusable CRUD operations for any Contentful content type
|
|
5
5
|
*/
|
|
6
|
+
import { smartFetch } from '../general/smartfetch';
|
|
7
|
+
import { buildUrl } from '../general/urlbuilder';
|
|
6
8
|
/**
|
|
7
9
|
* List all entries of a specific content type
|
|
8
10
|
*/
|
|
9
11
|
export async function listEntries(contentType, config) {
|
|
10
|
-
const { space_id,
|
|
12
|
+
const { space_id, management_access_token, environment = 'master' } = config;
|
|
11
13
|
try {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const url = buildUrl({
|
|
15
|
+
baseUrl: 'https://api.contentful.com',
|
|
16
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries'],
|
|
17
|
+
params: { content_type: contentType }
|
|
18
|
+
});
|
|
19
|
+
const data = await smartFetch(url, {
|
|
20
|
+
requestInit: {
|
|
21
|
+
headers: {
|
|
22
|
+
'Authorization': `Bearer ${management_access_token}`,
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
},
|
|
25
|
+
}
|
|
17
26
|
});
|
|
18
|
-
if (!response.ok) {
|
|
19
|
-
throw new Error(`Contentful API error: ${response.status}`);
|
|
20
|
-
}
|
|
21
|
-
const data = await response.json();
|
|
22
27
|
const entries = data.items || [];
|
|
23
28
|
return {
|
|
24
29
|
success: true,
|
|
@@ -37,30 +42,32 @@ export async function listEntries(contentType, config) {
|
|
|
37
42
|
* Get a single entry by ID
|
|
38
43
|
*/
|
|
39
44
|
export async function getEntryById(entryId, config) {
|
|
40
|
-
const { space_id,
|
|
45
|
+
const { space_id, management_access_token, environment = 'master' } = config;
|
|
41
46
|
try {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
'Content-Type': 'application/json',
|
|
46
|
-
},
|
|
47
|
+
const url = buildUrl({
|
|
48
|
+
baseUrl: 'https://api.contentful.com',
|
|
49
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries', entryId]
|
|
47
50
|
});
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
51
|
+
const entry = await smartFetch(url, {
|
|
52
|
+
requestInit: {
|
|
53
|
+
headers: {
|
|
54
|
+
'Authorization': `Bearer ${management_access_token}`,
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
},
|
|
54
57
|
}
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
const entry = await response.json();
|
|
58
|
+
});
|
|
58
59
|
return {
|
|
59
60
|
success: true,
|
|
60
61
|
entry,
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
catch (error) {
|
|
65
|
+
if (error.status === 404) {
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
message: 'Entry not found.',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
64
71
|
return {
|
|
65
72
|
success: false,
|
|
66
73
|
message: `Failed to get entry: ${error}`,
|
|
@@ -71,22 +78,25 @@ export async function getEntryById(entryId, config) {
|
|
|
71
78
|
* Search for entries by field value
|
|
72
79
|
*/
|
|
73
80
|
export async function searchEntriesByField(contentType, fieldName, fieldValue, config) {
|
|
74
|
-
const { space_id,
|
|
81
|
+
const { space_id, management_access_token, environment = 'master' } = config;
|
|
75
82
|
try {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
},
|
|
83
|
+
const url = buildUrl({
|
|
84
|
+
baseUrl: 'https://api.contentful.com',
|
|
85
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries'],
|
|
86
|
+
params: { content_type: contentType, [`fields.${fieldName}`]: fieldValue }
|
|
81
87
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
const entries = await smartFetch(url, {
|
|
89
|
+
requestInit: {
|
|
90
|
+
headers: {
|
|
91
|
+
'Authorization': `Bearer ${management_access_token}`,
|
|
92
|
+
'Content-Type': 'application/json',
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
const items = entries.items || [];
|
|
87
97
|
return {
|
|
88
98
|
success: true,
|
|
89
|
-
entries,
|
|
99
|
+
entries: items,
|
|
90
100
|
};
|
|
91
101
|
}
|
|
92
102
|
catch (error) {
|
|
@@ -101,43 +111,50 @@ export async function searchEntriesByField(contentType, fieldName, fieldValue, c
|
|
|
101
111
|
* Create a new entry
|
|
102
112
|
*/
|
|
103
113
|
export async function createEntry(contentType, fields, config, autoPublish = true) {
|
|
104
|
-
const { space_id,
|
|
114
|
+
const { space_id, management_access_token, environment = 'master' } = config;
|
|
105
115
|
try {
|
|
106
116
|
// Convert fields to Contentful format (with 'en-US' locale)
|
|
107
117
|
const contentfulFields = {};
|
|
108
118
|
for (const [key, value] of Object.entries(fields)) {
|
|
109
119
|
contentfulFields[key] = { 'en-US': value };
|
|
110
120
|
}
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
'Authorization': `Bearer ${delivery_access_token}`,
|
|
115
|
-
'Content-Type': 'application/vnd.contentful.management.v1+json',
|
|
116
|
-
'X-Contentful-Content-Type': contentType,
|
|
117
|
-
},
|
|
118
|
-
body: JSON.stringify({
|
|
119
|
-
fields: contentfulFields,
|
|
120
|
-
}),
|
|
121
|
+
const url = buildUrl({
|
|
122
|
+
baseUrl: 'https://api.contentful.com',
|
|
123
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries']
|
|
121
124
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
const newEntry = await createResponse.json();
|
|
127
|
-
// Publish if requested
|
|
128
|
-
if (autoPublish) {
|
|
129
|
-
await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries/${newEntry.sys.id}/published`, {
|
|
130
|
-
method: 'PUT',
|
|
125
|
+
const data = await smartFetch(url, {
|
|
126
|
+
requestInit: {
|
|
127
|
+
method: 'POST',
|
|
131
128
|
headers: {
|
|
132
|
-
'Authorization': `Bearer ${
|
|
133
|
-
'
|
|
129
|
+
'Authorization': `Bearer ${management_access_token}`,
|
|
130
|
+
'Content-Type': 'application/vnd.contentful.management.v1+json',
|
|
131
|
+
'X-Contentful-Content-Type': contentType,
|
|
134
132
|
},
|
|
133
|
+
body: JSON.stringify({
|
|
134
|
+
fields: contentfulFields,
|
|
135
|
+
}),
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
// Publish if requested
|
|
139
|
+
if (autoPublish) {
|
|
140
|
+
const publishUrl = buildUrl({
|
|
141
|
+
baseUrl: 'https://api.contentful.com',
|
|
142
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries', data.sys.id, 'published']
|
|
143
|
+
});
|
|
144
|
+
await smartFetch(publishUrl, {
|
|
145
|
+
requestInit: {
|
|
146
|
+
method: 'PUT',
|
|
147
|
+
headers: {
|
|
148
|
+
'Authorization': `Bearer ${management_access_token}`,
|
|
149
|
+
'X-Contentful-Version': data.sys.version.toString(),
|
|
150
|
+
},
|
|
151
|
+
}
|
|
135
152
|
});
|
|
136
153
|
}
|
|
137
154
|
return {
|
|
138
155
|
success: true,
|
|
139
156
|
message: 'Entry created successfully.',
|
|
140
|
-
entryId:
|
|
157
|
+
entryId: data.sys.id,
|
|
141
158
|
};
|
|
142
159
|
}
|
|
143
160
|
catch (error) {
|
|
@@ -151,7 +168,7 @@ export async function createEntry(contentType, fields, config, autoPublish = tru
|
|
|
151
168
|
* Update an existing entry
|
|
152
169
|
*/
|
|
153
170
|
export async function updateEntry(entryId, fields, config, autoPublish = true) {
|
|
154
|
-
const { space_id,
|
|
171
|
+
const { space_id, management_access_token, environment = 'master' } = config;
|
|
155
172
|
try {
|
|
156
173
|
// Get current entry to get version
|
|
157
174
|
const getResponse = await getEntryById(entryId, config);
|
|
@@ -167,30 +184,35 @@ export async function updateEntry(entryId, fields, config, autoPublish = true) {
|
|
|
167
184
|
for (const [key, value] of Object.entries(fields)) {
|
|
168
185
|
contentfulFields[key] = { 'en-US': value };
|
|
169
186
|
}
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
'X-Contentful-Version': currentEntry.sys.version.toString(),
|
|
176
|
-
},
|
|
177
|
-
body: JSON.stringify({
|
|
178
|
-
fields: contentfulFields,
|
|
179
|
-
}),
|
|
180
|
-
});
|
|
181
|
-
if (!updateResponse.ok) {
|
|
182
|
-
const error = await updateResponse.json();
|
|
183
|
-
throw new Error(`Update failed: ${JSON.stringify(error)}`);
|
|
184
|
-
}
|
|
185
|
-
const updatedEntry = await updateResponse.json();
|
|
186
|
-
// Publish if requested
|
|
187
|
-
if (autoPublish) {
|
|
188
|
-
await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries/${entryId}/published`, {
|
|
187
|
+
const updatedEntry = await smartFetch(buildUrl({
|
|
188
|
+
baseUrl: 'https://api.contentful.com',
|
|
189
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries', entryId],
|
|
190
|
+
}), {
|
|
191
|
+
requestInit: {
|
|
189
192
|
method: 'PUT',
|
|
190
193
|
headers: {
|
|
191
|
-
'Authorization': `Bearer ${
|
|
192
|
-
'
|
|
194
|
+
'Authorization': `Bearer ${management_access_token}`,
|
|
195
|
+
'Content-Type': 'application/vnd.contentful.management.v1+json',
|
|
196
|
+
'X-Contentful-Version': currentEntry.sys.version.toString(),
|
|
193
197
|
},
|
|
198
|
+
body: JSON.stringify({
|
|
199
|
+
fields: contentfulFields,
|
|
200
|
+
}),
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
// Publish if requested
|
|
204
|
+
if (autoPublish) {
|
|
205
|
+
await smartFetch(buildUrl({
|
|
206
|
+
baseUrl: 'https://api.contentful.com',
|
|
207
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries', entryId, 'published'],
|
|
208
|
+
}), {
|
|
209
|
+
requestInit: {
|
|
210
|
+
method: 'PUT',
|
|
211
|
+
headers: {
|
|
212
|
+
'Authorization': `Bearer ${management_access_token}`,
|
|
213
|
+
'X-Contentful-Version': updatedEntry.sys.version.toString(),
|
|
214
|
+
},
|
|
215
|
+
}
|
|
194
216
|
});
|
|
195
217
|
}
|
|
196
218
|
return {
|
|
@@ -210,7 +232,7 @@ export async function updateEntry(entryId, fields, config, autoPublish = true) {
|
|
|
210
232
|
* Delete an entry (unpublish first, then delete)
|
|
211
233
|
*/
|
|
212
234
|
export async function deleteEntry(entryId, config) {
|
|
213
|
-
const { space_id,
|
|
235
|
+
const { space_id, management_access_token, environment = 'master' } = config;
|
|
214
236
|
try {
|
|
215
237
|
// Get current entry to get version
|
|
216
238
|
const getResponse = await getEntryById(entryId, config);
|
|
@@ -222,24 +244,31 @@ export async function deleteEntry(entryId, config) {
|
|
|
222
244
|
}
|
|
223
245
|
const entry = getResponse.entry;
|
|
224
246
|
// Unpublish first
|
|
225
|
-
await
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
247
|
+
await smartFetch(buildUrl({
|
|
248
|
+
baseUrl: 'https://api.contentful.com',
|
|
249
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries', entry.sys.id, 'published'],
|
|
250
|
+
}), {
|
|
251
|
+
requestInit: {
|
|
252
|
+
method: 'PUT',
|
|
253
|
+
headers: {
|
|
254
|
+
'Authorization': `Bearer ${management_access_token}`,
|
|
255
|
+
'X-Contentful-Version': entry.sys.version.toString(),
|
|
256
|
+
},
|
|
257
|
+
}
|
|
231
258
|
});
|
|
232
259
|
// Delete the entry
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
260
|
+
await smartFetch(buildUrl({
|
|
261
|
+
baseUrl: 'https://api.contentful.com',
|
|
262
|
+
pathSegments: ['spaces', space_id, 'environments', environment, 'entries', entryId],
|
|
263
|
+
}), {
|
|
264
|
+
requestInit: {
|
|
265
|
+
method: 'DELETE',
|
|
266
|
+
headers: {
|
|
267
|
+
'Authorization': `Bearer ${management_access_token}`,
|
|
268
|
+
'X-Contentful-Version': (entry.sys.version + 1).toString(),
|
|
269
|
+
},
|
|
270
|
+
}
|
|
239
271
|
});
|
|
240
|
-
if (!deleteResponse.ok) {
|
|
241
|
-
throw new Error(`Delete failed: ${deleteResponse.status}`);
|
|
242
|
-
}
|
|
243
272
|
return {
|
|
244
273
|
success: true,
|
|
245
274
|
message: 'Entry deleted successfully.',
|
|
@@ -257,16 +286,18 @@ export async function deleteEntry(entryId, config) {
|
|
|
257
286
|
*/
|
|
258
287
|
export async function validateContentfulCredentials(credentials) {
|
|
259
288
|
try {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
289
|
+
await smartFetch(buildUrl({
|
|
290
|
+
baseUrl: 'https://api.contentful.com',
|
|
291
|
+
pathSegments: ['spaces', credentials.spaceId],
|
|
292
|
+
}), {
|
|
293
|
+
requestInit: {
|
|
294
|
+
method: 'GET',
|
|
295
|
+
headers: {
|
|
296
|
+
'Authorization': `Bearer ${credentials.accessToken}`,
|
|
297
|
+
'Content-Type': 'application/vnd.contentful.management.v1+json',
|
|
298
|
+
},
|
|
299
|
+
}
|
|
266
300
|
});
|
|
267
|
-
if (!response.ok) {
|
|
268
|
-
return { valid: false, error: 'Failed to access space' };
|
|
269
|
-
}
|
|
270
301
|
return { valid: true };
|
|
271
302
|
}
|
|
272
303
|
catch (error) {
|
|
@@ -279,42 +310,46 @@ export async function validateContentfulCredentials(credentials) {
|
|
|
279
310
|
export async function getContentTypes(credentials) {
|
|
280
311
|
const { spaceId, accessToken } = credentials;
|
|
281
312
|
// First get space info to find the default environment
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
313
|
+
await smartFetch(buildUrl({
|
|
314
|
+
baseUrl: 'https://api.contentful.com',
|
|
315
|
+
pathSegments: ['spaces', spaceId],
|
|
316
|
+
}), {
|
|
317
|
+
requestInit: {
|
|
318
|
+
method: 'GET',
|
|
319
|
+
headers: {
|
|
320
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
321
|
+
'Content-Type': 'application/vnd.contentful.management.v1+json',
|
|
322
|
+
},
|
|
323
|
+
}
|
|
288
324
|
});
|
|
289
|
-
if (!spaceResponse.ok) {
|
|
290
|
-
throw new Error('Failed to access space');
|
|
291
|
-
}
|
|
292
325
|
// Try different environment names - Contentful uses 'master' for older spaces, 'main' for newer ones
|
|
293
326
|
const environmentsToTry = ['master', 'main'];
|
|
294
|
-
let
|
|
327
|
+
let contentTypesData = null;
|
|
295
328
|
let lastError = null;
|
|
296
329
|
for (const env of environmentsToTry) {
|
|
297
330
|
try {
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
331
|
+
const data = await smartFetch(buildUrl({
|
|
332
|
+
baseUrl: 'https://api.contentful.com',
|
|
333
|
+
pathSegments: ['spaces', spaceId, 'environments', env, 'content_types'],
|
|
334
|
+
}), {
|
|
335
|
+
requestInit: {
|
|
336
|
+
method: 'GET',
|
|
337
|
+
headers: {
|
|
338
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
339
|
+
'Content-Type': 'application/vnd.contentful.management.v1+json',
|
|
340
|
+
},
|
|
341
|
+
}
|
|
304
342
|
});
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
343
|
+
contentTypesData = data;
|
|
344
|
+
break;
|
|
309
345
|
}
|
|
310
346
|
catch (error) {
|
|
311
347
|
lastError = error;
|
|
312
348
|
}
|
|
313
349
|
}
|
|
314
|
-
if (!
|
|
350
|
+
if (!contentTypesData) {
|
|
315
351
|
throw new Error(`Failed to fetch content types: ${lastError}`);
|
|
316
352
|
}
|
|
317
|
-
const contentTypesData = await contentTypesResponse.json();
|
|
318
353
|
return contentTypesData.items || [];
|
|
319
354
|
}
|
|
320
355
|
/**
|
|
@@ -324,32 +359,34 @@ export async function migrateContentType(sourceCredentials, destCredentials, con
|
|
|
324
359
|
try {
|
|
325
360
|
// Get content type from source
|
|
326
361
|
const sourceEnv = sourceCredentials.environment || 'master';
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
362
|
+
const contentType = await smartFetch(buildUrl({
|
|
363
|
+
baseUrl: 'https://api.contentful.com',
|
|
364
|
+
pathSegments: ['spaces', sourceCredentials.spaceId, 'environments', sourceEnv, 'content_types', contentTypeId],
|
|
365
|
+
}), {
|
|
366
|
+
requestInit: {
|
|
367
|
+
method: 'GET',
|
|
368
|
+
headers: {
|
|
369
|
+
'Authorization': `Bearer ${sourceCredentials.accessToken}`,
|
|
370
|
+
'Content-Type': 'application/vnd.contentful.management.v1+json',
|
|
371
|
+
},
|
|
372
|
+
}
|
|
333
373
|
});
|
|
334
|
-
if (!sourceResponse.ok) {
|
|
335
|
-
throw new Error('Failed to fetch content type from source');
|
|
336
|
-
}
|
|
337
|
-
const contentType = await sourceResponse.json();
|
|
338
374
|
// Create content type in destination
|
|
339
375
|
const destEnv = destCredentials.environment || 'master';
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
376
|
+
await smartFetch(buildUrl({
|
|
377
|
+
baseUrl: 'https://api.contentful.com',
|
|
378
|
+
pathSegments: ['spaces', destCredentials.spaceId, 'environments', destEnv, 'content_types'],
|
|
379
|
+
}), {
|
|
380
|
+
requestInit: {
|
|
381
|
+
method: 'POST',
|
|
382
|
+
headers: {
|
|
383
|
+
'Authorization': `Bearer ${destCredentials.accessToken}`,
|
|
384
|
+
'Content-Type': 'application/vnd.contentful.management.v1+json',
|
|
385
|
+
'X-Contentful-Version': '1',
|
|
386
|
+
},
|
|
387
|
+
body: JSON.stringify(contentType),
|
|
388
|
+
}
|
|
348
389
|
});
|
|
349
|
-
if (!createResponse.ok) {
|
|
350
|
-
const errorData = await createResponse.json();
|
|
351
|
-
throw new Error(`Failed to create content type: ${errorData.message}`);
|
|
352
|
-
}
|
|
353
390
|
return { success: true };
|
|
354
391
|
}
|
|
355
392
|
catch (error) {
|
|
@@ -3,6 +3,8 @@ import { mergeDeep } from '../general/utilities';
|
|
|
3
3
|
import { hashCode } from '../general/utilities';
|
|
4
4
|
import { CacheManager } from '../general/cache-manager';
|
|
5
5
|
import { getDomain } from '../general/utilities';
|
|
6
|
+
import { smartFetch } from '../general/smartfetch';
|
|
7
|
+
import { buildUrl } from '../general/urlbuilder';
|
|
6
8
|
// Flickr API base URL - non-secret configuration
|
|
7
9
|
const FLICKR_API_BASE_URL = 'https://api.flickr.com/services/rest/?';
|
|
8
10
|
const defaultFlickr = {
|
|
@@ -25,13 +27,11 @@ const defaultFlickr = {
|
|
|
25
27
|
};
|
|
26
28
|
// Utility to build the final Flickr API URL, using proxy if available
|
|
27
29
|
function buildFlickrApiUrl(flickr) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
queryParams += (queryParams.length === 0) ? prop + '=' + value : '&' + prop + '=' + value;
|
|
30
|
+
// Use buildUrl to construct the Flickr REST API URL from parameters
|
|
31
|
+
const apiUrl = buildUrl({
|
|
32
|
+
baseUrl: flickr.baseURL,
|
|
33
|
+
params: flickr.urlProps,
|
|
33
34
|
});
|
|
34
|
-
const apiUrl = baseUrl + queryParams;
|
|
35
35
|
// Prefer flickr.proxyURL, then globalConfig.proxyUrl, else direct
|
|
36
36
|
if (flickr.proxyURL) {
|
|
37
37
|
return flickr.proxyURL + encodeURIComponent(apiUrl);
|
|
@@ -99,18 +99,16 @@ export function GetFlickrData(props) {
|
|
|
99
99
|
});
|
|
100
100
|
const fetchFlickrData = async () => {
|
|
101
101
|
const cacheKey = hashCode(myURL);
|
|
102
|
-
let cached = flickrCache.get(cacheKey);
|
|
103
|
-
if (cached) {
|
|
104
|
-
if (debug)
|
|
105
|
-
console.log('Flickr cache hit:', cacheKey);
|
|
106
|
-
return cached;
|
|
107
|
-
}
|
|
108
102
|
try {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
103
|
+
const jsonData = await smartFetch(myURL, {
|
|
104
|
+
cache: flickrCache,
|
|
105
|
+
cacheKey,
|
|
106
|
+
debug,
|
|
107
|
+
});
|
|
108
|
+
// Check if Flickr API returned an error response
|
|
109
|
+
if (jsonData.stat === 'fail') {
|
|
110
|
+
throw new Error(`Flickr API error: ${jsonData.message || 'Unknown error'}`);
|
|
112
111
|
}
|
|
113
|
-
const jsonData = await response.json();
|
|
114
112
|
let myFlickrImages = [];
|
|
115
113
|
if (jsonData.photos) {
|
|
116
114
|
// photos for tags - flickr.photos.search
|
|
@@ -121,12 +119,11 @@ export function GetFlickrData(props) {
|
|
|
121
119
|
myFlickrImages = jsonData.photoset.photo;
|
|
122
120
|
}
|
|
123
121
|
else {
|
|
124
|
-
|
|
122
|
+
throw new Error('No photos or photoset found in Flickr API response');
|
|
125
123
|
}
|
|
126
124
|
myFlickrImages.sort((a, b) => {
|
|
127
125
|
return new Date(b.datetaken).getTime() - new Date(a.datetaken).getTime();
|
|
128
126
|
}); // b - a for reverse sort
|
|
129
|
-
flickrCache.set(cacheKey, myFlickrImages);
|
|
130
127
|
if (debug)
|
|
131
128
|
console.log('Flickr Cards:', myFlickrImages);
|
|
132
129
|
return myFlickrImages;
|
|
@@ -134,10 +131,6 @@ export function GetFlickrData(props) {
|
|
|
134
131
|
catch (err) {
|
|
135
132
|
console.log('Error fetching Flickr data:', err);
|
|
136
133
|
}
|
|
137
|
-
finally {
|
|
138
|
-
if (debug)
|
|
139
|
-
console.log('Flickr data fetch completed');
|
|
140
|
-
}
|
|
141
134
|
};
|
|
142
135
|
return fetchFlickrData();
|
|
143
136
|
}
|