contentoh-components-library 21.5.99 → 21.6.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/dist/components/atoms/GeneralButton/styles.js +1 -1
- package/dist/components/atoms/GeneralInput/index.js +249 -54
- package/dist/components/atoms/GeneralInput/styles.js +7 -3
- package/dist/components/atoms/InputFormatter/index.js +223 -68
- package/dist/components/atoms/InputFormatter/styles.js +20 -4
- package/dist/components/molecules/StatusAsignationInfo/index.js +11 -1
- package/dist/components/molecules/TabsMenu/index.js +13 -1
- package/dist/components/molecules/TagAndInput/index.js +364 -24
- package/dist/components/molecules/TagAndInput/styles.js +2 -2
- package/dist/components/organisms/FullProductNameHeader/index.js +6 -22
- package/dist/components/organisms/InputGroup/index.js +22 -18
- package/dist/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +150 -337
- package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.context.js +15 -15
- package/dist/components/pages/ProviderProductEdition/index.js +393 -352
- package/dist/components/pages/ProviderProductEdition/utils.js +1 -0
- package/dist/components/pages/RetailerProductEdition/RetailerProductEdition.stories.js +125 -211
- package/dist/components/pages/RetailerProductEdition/index.js +1743 -2239
- package/dist/components/pages/RetailerProductEdition/styles.js +4 -2
- package/dist/components/pages/RetailerProductEdition/utils.js +251 -2
- package/package.json +4 -2
- package/src/ai/utils/compare-strings.js +45 -0
- package/src/assets/images/Icons/arrow.png +0 -0
- package/src/assets/images/Icons/cancel.png +0 -0
- package/src/assets/images/Icons/ia-icon.png +0 -0
- package/src/assets/images/Icons/loading.svg +5 -0
- package/src/assets/images/Icons/reload.png +0 -0
- package/src/components/atoms/GeneralButton/styles.js +4 -0
- package/src/components/atoms/GeneralInput/index.js +241 -60
- package/src/components/atoms/GeneralInput/styles.js +81 -0
- package/src/components/atoms/InputFormatter/index.js +200 -51
- package/src/components/atoms/InputFormatter/styles.js +284 -0
- package/src/components/atoms/RetailerSelector/RetailerSelector.stories.js +10 -0
- package/src/components/atoms/RetailerSelector/index.js +3 -0
- package/src/components/atoms/RetailerSelector/styles.js +0 -0
- package/src/components/molecules/StatusAsignationInfo/index.js +9 -1
- package/src/components/molecules/TabsMenu/index.js +12 -0
- package/src/components/molecules/TagAndInput/index.js +294 -21
- package/src/components/molecules/TagAndInput/styles.js +59 -17
- package/src/components/organisms/ChangeStatusModal/index.jsx +488 -0
- package/src/components/organisms/ChangeStatusModal/styles.js +333 -0
- package/src/components/organisms/FullProductNameHeader/index.js +4 -28
- package/src/components/organisms/FullTabsMenu/index.js +1 -1
- package/src/components/organisms/InputGroup/index.js +12 -4
- package/src/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +174 -202
- package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx +14 -14
- package/src/components/pages/ProviderProductEdition/index.js +486 -417
- package/src/components/pages/ProviderProductEdition/utils.js +2 -2
- package/src/components/pages/RetailerProductEdition/RetailerProductEdition.stories.js +136 -243
- package/src/components/pages/RetailerProductEdition/context/provider-product-edition.context.jsx +575 -0
- package/src/components/pages/RetailerProductEdition/context/provider-product-edition.reducer.js +62 -0
- package/src/components/pages/RetailerProductEdition/context/reducers/active-state.js +344 -0
- package/src/components/pages/RetailerProductEdition/context/reducers/inputs.js +155 -0
- package/src/components/pages/RetailerProductEdition/context/reducers/product.js +114 -0
- package/src/components/pages/RetailerProductEdition/context/reducers/system.js +60 -0
- package/src/components/pages/RetailerProductEdition/index.js +1580 -1719
- package/src/components/pages/RetailerProductEdition/index_old.js +1979 -0
- package/src/components/pages/RetailerProductEdition/stories/Auditor.stories.js +101 -0
- package/src/components/pages/RetailerProductEdition/stories/ImageEditor.stories.js +115 -0
- package/src/components/pages/RetailerProductEdition/stories/TextEditor.stories.js +174 -0
- package/src/components/pages/RetailerProductEdition/styles.js +67 -2
- package/src/components/pages/RetailerProductEdition/utils.js +240 -0
- package/src/contexts/AiProductEdition.jsx +347 -0
- package/src/global-files/statusDictionary.js +103 -0
|
@@ -69,3 +69,243 @@ export const sendMessage = async (data) => {
|
|
|
69
69
|
paramsHeader
|
|
70
70
|
);
|
|
71
71
|
};
|
|
72
|
+
|
|
73
|
+
export const normalizeProduct = (product) => {
|
|
74
|
+
// Handle case where product might be wrapped in a product property
|
|
75
|
+
const productData = product.product || product;
|
|
76
|
+
const article = productData.article || {};
|
|
77
|
+
|
|
78
|
+
const categoryRetailer =
|
|
79
|
+
productData.categoryRetailer || article.categoryRetailer;
|
|
80
|
+
|
|
81
|
+
const categoryRetailerNormalized = categoryRetailer
|
|
82
|
+
? categoryRetailer.map((rel) => ({
|
|
83
|
+
id_retailer: rel.id_retailer || rel.retailer_id,
|
|
84
|
+
retailer: rel.retailer_name || rel.retailerName,
|
|
85
|
+
name: rel.retailer_name || rel.retailerName,
|
|
86
|
+
id_category: rel.id_category,
|
|
87
|
+
category: rel.category_name || rel.categoryName,
|
|
88
|
+
image: `https://content-management-images.s3.amazonaws.com/retailers/${
|
|
89
|
+
rel.id_retailer || rel.retailer_id
|
|
90
|
+
}.png`,
|
|
91
|
+
}))
|
|
92
|
+
: null;
|
|
93
|
+
|
|
94
|
+
// extraer los retailer_id de statusByRetailer donde task_group_id no sea nulo
|
|
95
|
+
const retailersIdsInOrder = productData.statusByRetailer
|
|
96
|
+
? productData.statusByRetailer.map((status) => status.retailer_id)
|
|
97
|
+
: [];
|
|
98
|
+
|
|
99
|
+
const categoryRetailerInOrder = categoryRetailerNormalized.filter((rel) =>
|
|
100
|
+
retailersIdsInOrder.includes(rel.id_retailer)
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
id_article: productData.id_article || article.id_article || 0,
|
|
105
|
+
upc: productData.upc || article.upc || null,
|
|
106
|
+
sku: productData.sku || article.sku || null,
|
|
107
|
+
name: productData.name || article.name || null,
|
|
108
|
+
timestamp: productData.timestamp || article.timestamp || null,
|
|
109
|
+
id_user: productData.id_user || article.id_user || null,
|
|
110
|
+
categoryName: productData.category || article.category || null,
|
|
111
|
+
id_category: productData.id_category || article.id_category || null,
|
|
112
|
+
retailersAvailable: productData.retailersAvailable || null,
|
|
113
|
+
categoryRetailer: categoryRetailerNormalized || null,
|
|
114
|
+
percentages: productData.percentages || null,
|
|
115
|
+
summary: productData.summary || null,
|
|
116
|
+
version: productData.version || 0,
|
|
117
|
+
id_order: productData.orderId || 0,
|
|
118
|
+
status: productData.status || null,
|
|
119
|
+
datasheet_status: productData.datasheet_status || null,
|
|
120
|
+
description_status: productData.description_status || null,
|
|
121
|
+
images_status: productData.images_status || null,
|
|
122
|
+
brand: productData.brand || null,
|
|
123
|
+
services: productData.services || null,
|
|
124
|
+
country: article.country || null,
|
|
125
|
+
id_company: article.company_id || null,
|
|
126
|
+
company: article.company_name || null,
|
|
127
|
+
id_datasheet_especialist: article.id_datasheet_especialist || 0,
|
|
128
|
+
id_datasheet_facilitator: article.id_datasheet_facilitator || null,
|
|
129
|
+
id_description_especialist: article.id_description_especialist || 0,
|
|
130
|
+
id_description_facilitator: article.id_description_facilitator || null,
|
|
131
|
+
id_images_especialist: article.id_images_especialist || 0,
|
|
132
|
+
id_images_facilitator: article.id_images_facilitator || null,
|
|
133
|
+
id_auditor: article.id_auditor || 0,
|
|
134
|
+
statusByRetailer: productData.statusByRetailer || null,
|
|
135
|
+
categoryRetailerInOrder: categoryRetailerInOrder || null,
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const TAB_TO_CONCEPT = {
|
|
140
|
+
Descripción: "description",
|
|
141
|
+
"Ficha técnica": "datasheet",
|
|
142
|
+
Imágenes: "images",
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const CONCEPT_TO_TAB = {
|
|
146
|
+
description: "Descripción",
|
|
147
|
+
datasheet: "Ficha técnica",
|
|
148
|
+
images: "Imágenes",
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export const getConceptByTab = (tab) => TAB_TO_CONCEPT[tab];
|
|
152
|
+
|
|
153
|
+
export const translateConcept = (concept) => CONCEPT_TO_TAB[concept];
|
|
154
|
+
|
|
155
|
+
export const getStatusArrayByRole = (idRole) => {
|
|
156
|
+
switch (idRole) {
|
|
157
|
+
case 7:
|
|
158
|
+
case 8:
|
|
159
|
+
return ["PA", "AS", "CA", "RC", "RA", "RP", "RCA"];
|
|
160
|
+
case 4:
|
|
161
|
+
case 5:
|
|
162
|
+
return ["RC", "AC", "AA", "AP", "ACA"];
|
|
163
|
+
case 6:
|
|
164
|
+
return ["RP", "RCA", "AC", "RA"];
|
|
165
|
+
default:
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const getConceptsByRole = (idRole) => {
|
|
171
|
+
switch (idRole) {
|
|
172
|
+
case 4:
|
|
173
|
+
return ["description", "datasheet"];
|
|
174
|
+
case 5:
|
|
175
|
+
return ["images"];
|
|
176
|
+
default:
|
|
177
|
+
return ["description", "datasheet", "images"];
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export const buildCollaboratorAssignations = (product, users) => ({
|
|
182
|
+
Descripción: {
|
|
183
|
+
assignations: [
|
|
184
|
+
{
|
|
185
|
+
collaboratorType: "especialist",
|
|
186
|
+
id: product.id_description_especialist,
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
collaboratorType: "facilitator",
|
|
190
|
+
id: product.id_description_facilitator,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
collaborators: {
|
|
194
|
+
especialist: users[0] || [],
|
|
195
|
+
facilitator: users[2] || [],
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
"Ficha técnica": {
|
|
199
|
+
assignations: [
|
|
200
|
+
{
|
|
201
|
+
collaboratorType: "especialist",
|
|
202
|
+
id: product.id_datasheet_especialist,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
collaboratorType: "facilitator",
|
|
206
|
+
id: product.id_datasheet_facilitator,
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
collaborators: {
|
|
210
|
+
especialist: users[0] || [],
|
|
211
|
+
facilitator: users[2] || [],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
Imágenes: {
|
|
215
|
+
assignations: [
|
|
216
|
+
{
|
|
217
|
+
collaboratorType: "especialist",
|
|
218
|
+
id: product.id_images_especialist,
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
collaboratorType: "facilitator",
|
|
222
|
+
id: product.id_images_facilitator,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
collaborators: {
|
|
226
|
+
especialist: users[1] || [],
|
|
227
|
+
facilitator: users[3] || [],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
export const calculateRequiredNull = (services, servicesData, activeRetailerId) => {
|
|
233
|
+
const result = {
|
|
234
|
+
"Ficha técnica": 0,
|
|
235
|
+
Descripción: 0,
|
|
236
|
+
Imágenes: 0,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const isServiceRequested = (retailerId, concept) =>
|
|
240
|
+
servicesData?.some(
|
|
241
|
+
(srv) => srv.id_retailer === retailerId && srv.service === concept
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Ficha técnica: contar inputs requeridos sin valor
|
|
245
|
+
const datasheetInputs = services[0]?.inputs || {};
|
|
246
|
+
const datasheetsByRetailer = Object.values(services[0]).filter(
|
|
247
|
+
(item) => item?.retailer?.id === activeRetailerId
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
datasheetsByRetailer.forEach((datasheet) => {
|
|
251
|
+
if (!isServiceRequested(datasheet.retailer?.id, "datasheet")) return;
|
|
252
|
+
|
|
253
|
+
Object.values(datasheet.data || {}).forEach((dataGroup) => {
|
|
254
|
+
dataGroup.inputs?.forEach((inputId) => {
|
|
255
|
+
const input = datasheetInputs[inputId];
|
|
256
|
+
if (
|
|
257
|
+
input?.required &&
|
|
258
|
+
input?.id_retailer === activeRetailerId &&
|
|
259
|
+
!input?.value
|
|
260
|
+
) {
|
|
261
|
+
result["Ficha técnica"]++;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Descripción: contar inputs requeridos sin valor
|
|
268
|
+
const htmlTagsRegex = /(<\/?p>)|(<\/?strong>)|(<br>)/gm;
|
|
269
|
+
const descriptions = services[1]?.filter(
|
|
270
|
+
(desc) => desc.id === activeRetailerId
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
descriptions?.forEach((description) => {
|
|
274
|
+
if (!isServiceRequested(description.id, "description")) return;
|
|
275
|
+
|
|
276
|
+
description.inputs?.forEach((input) => {
|
|
277
|
+
const valueClean = input.value?.replace(htmlTagsRegex, "") || "";
|
|
278
|
+
if (input.required && !valueClean) {
|
|
279
|
+
result["Descripción"]++;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Imágenes: contar imágenes requeridas sin valor
|
|
285
|
+
const retailerMandatories = services[2]?.retailerMandatories?.flat() || [];
|
|
286
|
+
const requestedRetailers = retailerMandatories.filter((rm) =>
|
|
287
|
+
servicesData?.some((srv) => srv.id_retailer === rm.id_retailer)
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const requiredImageIds = new Set(
|
|
291
|
+
requestedRetailers
|
|
292
|
+
.filter((rm) =>
|
|
293
|
+
services[2]?.inputs?.some(
|
|
294
|
+
(input) => input.id === rm.id_image && input.required === 1
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
.map((rm) => rm.id_image)
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const existingImageIds = new Set(
|
|
301
|
+
services[2]?.values?.map((img) => img.image_id) || []
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
requiredImageIds.forEach((imageId) => {
|
|
305
|
+
if (!existingImageIds.has(imageId)) {
|
|
306
|
+
result["Imágenes"]++;
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return result;
|
|
311
|
+
};
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { useContext, useEffect, useState } from "react";
|
|
2
|
+
import { createContext } from "react";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
|
|
5
|
+
export const AiProductEdition = createContext();
|
|
6
|
+
|
|
7
|
+
export const useAiProductEdition = () => {
|
|
8
|
+
const context = useContext(AiProductEdition);
|
|
9
|
+
|
|
10
|
+
if (!context) return {};
|
|
11
|
+
|
|
12
|
+
return context;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const AiProductEditionProvider = ({
|
|
16
|
+
children,
|
|
17
|
+
isCreatorsEdition = false,
|
|
18
|
+
user = {},
|
|
19
|
+
token = "",
|
|
20
|
+
state
|
|
21
|
+
}) => {
|
|
22
|
+
|
|
23
|
+
const isCreators = isCreatorsEdition;
|
|
24
|
+
|
|
25
|
+
const productSelected = state.product;
|
|
26
|
+
const activeRetailer = state.active_retailer;
|
|
27
|
+
const datasheetInputs = state.datasheets_inputs?.[1];
|
|
28
|
+
const imagesData = state.images_values;
|
|
29
|
+
|
|
30
|
+
const [product, setProduct] = useState(null);
|
|
31
|
+
|
|
32
|
+
const [parsedDatasheet, setParsedDatasheet] = useState([]);
|
|
33
|
+
const [parsedImages, setParsedImages] = useState([]);
|
|
34
|
+
|
|
35
|
+
const [suggestions, setSuggestions] = useState({});
|
|
36
|
+
const [currentSuggestion, setCurrentSuggestion] = useState({});
|
|
37
|
+
|
|
38
|
+
const [isAiAvailable, setIsAiAvailable] = useState(false);
|
|
39
|
+
|
|
40
|
+
const [inputsGeneratedWithAi, setInputsGeneratedWithAi] = useState({});
|
|
41
|
+
const [inputsUsingAi, setInputsUsingAi] = useState({});
|
|
42
|
+
|
|
43
|
+
const MAX_CREDITS = 10;
|
|
44
|
+
const COOLDOWN_MS = 5 * 60 * 1000;
|
|
45
|
+
const RATE_LIMIT_KEY = "ai_generation_limit_data";
|
|
46
|
+
|
|
47
|
+
const checkAndManageRateLimit = (currentArticleId) => {
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
const storedData = localStorage.getItem(RATE_LIMIT_KEY);
|
|
50
|
+
|
|
51
|
+
let allRateData = storedData ? JSON.parse(storedData) : {};
|
|
52
|
+
|
|
53
|
+
let productTimestamps = allRateData[currentArticleId] || [];
|
|
54
|
+
|
|
55
|
+
productTimestamps = productTimestamps.filter(timestamp => (now - timestamp) < COOLDOWN_MS);
|
|
56
|
+
|
|
57
|
+
if (productTimestamps.length >= MAX_CREDITS) {
|
|
58
|
+
|
|
59
|
+
allRateData[currentArticleId] = productTimestamps;
|
|
60
|
+
localStorage.setItem(RATE_LIMIT_KEY, JSON.stringify(allRateData));
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
allowed: false,
|
|
64
|
+
message: "Has alcanzado el limite de intentos frecuentes para este producto. Por favor, espera unos minutos."
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
productTimestamps.push(now);
|
|
70
|
+
allRateData[currentArticleId] = productTimestamps;
|
|
71
|
+
|
|
72
|
+
localStorage.setItem(RATE_LIMIT_KEY, JSON.stringify(allRateData));
|
|
73
|
+
|
|
74
|
+
return { allowed: true };
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
function setCurrentSuggestionValue({
|
|
78
|
+
inputId,
|
|
79
|
+
index,
|
|
80
|
+
value
|
|
81
|
+
}) {
|
|
82
|
+
setCurrentSuggestion(prev => ({
|
|
83
|
+
...prev,
|
|
84
|
+
[inputId]: {
|
|
85
|
+
index,
|
|
86
|
+
value
|
|
87
|
+
}
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function clearCurrentSuggestions() {
|
|
92
|
+
setCurrentSuggestion({});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function regenerateProductSuggestions ({
|
|
96
|
+
inputName,
|
|
97
|
+
currentValue,
|
|
98
|
+
description,
|
|
99
|
+
maxChar,
|
|
100
|
+
type,
|
|
101
|
+
|
|
102
|
+
articleId,
|
|
103
|
+
versionId,
|
|
104
|
+
descriptionId,
|
|
105
|
+
attributeId
|
|
106
|
+
}) {
|
|
107
|
+
|
|
108
|
+
if(!description || !maxChar || !type) return console.log("Error: No se obtuvieron los parametros obligatorios");
|
|
109
|
+
|
|
110
|
+
const newSuggestions = await generateProductSuggestions({
|
|
111
|
+
inputName,
|
|
112
|
+
currentValue,
|
|
113
|
+
description,
|
|
114
|
+
maxChar,
|
|
115
|
+
type,
|
|
116
|
+
|
|
117
|
+
articleId,
|
|
118
|
+
versionId,
|
|
119
|
+
descriptionId,
|
|
120
|
+
attributeId
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if(newSuggestions?.error) return {
|
|
124
|
+
error: newSuggestions.error
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if(!Array.isArray(newSuggestions) || newSuggestions.length === 0)
|
|
128
|
+
return console.log("Error: No se obtuvieron nuevas sugerencias");
|
|
129
|
+
|
|
130
|
+
if(!newSuggestions) return console.log("Error: No exiten resultados nuevos");
|
|
131
|
+
|
|
132
|
+
return newSuggestions;
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function generateProductSuggestions ({
|
|
137
|
+
inputName = "",
|
|
138
|
+
currentValue = "",
|
|
139
|
+
description = "",
|
|
140
|
+
maxChar = 100,
|
|
141
|
+
type = 'description',
|
|
142
|
+
|
|
143
|
+
articleId,
|
|
144
|
+
versionId,
|
|
145
|
+
descriptionId,
|
|
146
|
+
attributeId
|
|
147
|
+
}) {
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
|
|
151
|
+
if(!isAiAvailable) return;
|
|
152
|
+
|
|
153
|
+
const rateLimitStatus = checkAndManageRateLimit(articleId);
|
|
154
|
+
|
|
155
|
+
if (!rateLimitStatus.allowed) {
|
|
156
|
+
return {
|
|
157
|
+
error: rateLimitStatus.message,
|
|
158
|
+
isRateLimitInfo: true
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if(!product)
|
|
163
|
+
throw new Error("El producto no está definido");
|
|
164
|
+
|
|
165
|
+
if(!Array.isArray(parsedDatasheet) || parsedDatasheet.length === 0)
|
|
166
|
+
throw new Error("No es encontró la ficha técnica");
|
|
167
|
+
|
|
168
|
+
if(!Array.isArray(parsedImages) || parsedImages.length === 0)
|
|
169
|
+
throw new Error("No se encontraron imágenes para la cadena seleccionada");
|
|
170
|
+
|
|
171
|
+
const {
|
|
172
|
+
upc,
|
|
173
|
+
productName,
|
|
174
|
+
retailer,
|
|
175
|
+
category
|
|
176
|
+
} = product;
|
|
177
|
+
|
|
178
|
+
const version = state?.product?.version;
|
|
179
|
+
|
|
180
|
+
if (!upc || !description || !productName || !category || !retailer || !articleId || !version || (!descriptionId && !attributeId))
|
|
181
|
+
throw new Error("Faltan parámetros obligatorios para generar sugerencias de IA");
|
|
182
|
+
|
|
183
|
+
const payload = {
|
|
184
|
+
upc,
|
|
185
|
+
attributeTitle: inputName,
|
|
186
|
+
attributeDescription: description,
|
|
187
|
+
productName,
|
|
188
|
+
currentValue,
|
|
189
|
+
categoryName: category,
|
|
190
|
+
retailerName: retailer,
|
|
191
|
+
datasheet: parsedDatasheet,
|
|
192
|
+
images: parsedImages,
|
|
193
|
+
maxChar: maxChar,
|
|
194
|
+
|
|
195
|
+
articleId,
|
|
196
|
+
retailerId: activeRetailer?.id_retailer,
|
|
197
|
+
versionId: version,
|
|
198
|
+
descriptionId,
|
|
199
|
+
attributeId
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const { data } = await axios.post(process.env.REACT_APP_GENERATE_AI_ATTRIBUTES, payload, {
|
|
203
|
+
headers: {
|
|
204
|
+
Authorization: token
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const errorsRaw = JSON.parse(data?.body)?.error;
|
|
209
|
+
|
|
210
|
+
const errorMessage = errorsRaw ? JSON.parse(errorsRaw)?.error?.message : null;
|
|
211
|
+
|
|
212
|
+
if(errorMessage) throw new Error(errorMessage ?? 'Error desconocido')
|
|
213
|
+
|
|
214
|
+
const results = JSON.parse(data?.body)?.data ?? [];
|
|
215
|
+
|
|
216
|
+
if(!results)
|
|
217
|
+
throw new Error("No se encontraron resultados");
|
|
218
|
+
|
|
219
|
+
return results;
|
|
220
|
+
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.log("Error generating AI suggestions:", error);
|
|
223
|
+
return {
|
|
224
|
+
error: error?.message ?? "Error generating AI suggestions"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Verificamos si los servicios de ficha técnica e imágenes estan completos
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
|
|
233
|
+
if (!productSelected || !isCreators) return;
|
|
234
|
+
|
|
235
|
+
const {
|
|
236
|
+
name: productName,
|
|
237
|
+
upc: upc,
|
|
238
|
+
categoryRetailer
|
|
239
|
+
} = productSelected;
|
|
240
|
+
|
|
241
|
+
if(!Array.isArray(categoryRetailer)) return console.log("Error: categoryRetailer no es un array");
|
|
242
|
+
|
|
243
|
+
const currentCategory = categoryRetailer.find(retailer => retailer?.id_retailer === activeRetailer?.id_retailer);
|
|
244
|
+
|
|
245
|
+
if(!currentCategory) return console.log("Error: No se encontro la categoría");
|
|
246
|
+
|
|
247
|
+
const {
|
|
248
|
+
retailer,
|
|
249
|
+
category
|
|
250
|
+
} = currentCategory;
|
|
251
|
+
|
|
252
|
+
//Checamos si el servicio de ficha técnica e imagenes esta completo
|
|
253
|
+
|
|
254
|
+
const currentRetailerPercentages = productSelected?.percentages?.find(retailer => retailer?.id_retailer === activeRetailer?.id_retailer);
|
|
255
|
+
|
|
256
|
+
if(!currentRetailerPercentages) return console.log("Error: No hay porcentajes disponibles");
|
|
257
|
+
|
|
258
|
+
const {
|
|
259
|
+
required: datasheetRequiredPercent,
|
|
260
|
+
} = currentRetailerPercentages?.datasheet;
|
|
261
|
+
|
|
262
|
+
const {
|
|
263
|
+
required: imagesRequiredPercent,
|
|
264
|
+
} = currentRetailerPercentages?.images;
|
|
265
|
+
|
|
266
|
+
const aiServiceAvailable = (datasheetRequiredPercent >= 100) && (imagesRequiredPercent >= 100) && [1,4,6,7].some(allowed => allowed = user?.id_role);
|
|
267
|
+
|
|
268
|
+
setIsAiAvailable(aiServiceAvailable);
|
|
269
|
+
|
|
270
|
+
setProduct({
|
|
271
|
+
upc,
|
|
272
|
+
productName,
|
|
273
|
+
retailer,
|
|
274
|
+
category,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
}, [productSelected]);
|
|
278
|
+
|
|
279
|
+
// Inicializamos la ficha técnica con el objetivo de posteriormente pasarlo a la generación con IA
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
|
|
282
|
+
if (!datasheetInputs || !isCreators) return;
|
|
283
|
+
|
|
284
|
+
const datasheetToArray = Object.values(datasheetInputs);
|
|
285
|
+
|
|
286
|
+
const normalizedDatasheet = datasheetToArray.map(attribute => ({
|
|
287
|
+
description: attribute?.description,
|
|
288
|
+
name: attribute?.name,
|
|
289
|
+
type: attribute?.type,
|
|
290
|
+
value: attribute?.value
|
|
291
|
+
}));
|
|
292
|
+
|
|
293
|
+
setParsedDatasheet(normalizedDatasheet);
|
|
294
|
+
|
|
295
|
+
}, [datasheetInputs]);
|
|
296
|
+
|
|
297
|
+
// Inicializamos las imágenes con el objetivo de posteriormente pasarlo a la generación con IA
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
|
|
300
|
+
if(!isCreators) return;
|
|
301
|
+
|
|
302
|
+
const currentRetailerImageInputs = imagesData?.inputsByRetailer?.[0]?.filter(input => input?.id_retailer === activeRetailer?.id_retailer);
|
|
303
|
+
|
|
304
|
+
if(!Array.isArray(currentRetailerImageInputs) || currentRetailerImageInputs.length === 0) return console.log('No existen imágenes para la cadena seleccionada')
|
|
305
|
+
|
|
306
|
+
const allProductImages = imagesData?.values;
|
|
307
|
+
|
|
308
|
+
if(!Array.isArray(allProductImages) || allProductImages.length === 0) return console.log("No existen imágenes para el producto");
|
|
309
|
+
|
|
310
|
+
//Obtenemos las imágenes para el retailer seleccionado
|
|
311
|
+
const currentParsedImages = allProductImages.filter(image => {
|
|
312
|
+
|
|
313
|
+
const imageId = image?.image_id;
|
|
314
|
+
const foundedImage = currentRetailerImageInputs.some(retailerImage => retailerImage?.id_image === imageId);
|
|
315
|
+
|
|
316
|
+
return foundedImage;
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
setParsedImages(currentParsedImages);
|
|
320
|
+
|
|
321
|
+
}, [imagesData]);
|
|
322
|
+
|
|
323
|
+
return (
|
|
324
|
+
<AiProductEdition.Provider
|
|
325
|
+
value={{
|
|
326
|
+
isCreators: isCreators,
|
|
327
|
+
suggestions,
|
|
328
|
+
currentSuggestion,
|
|
329
|
+
isAiAvailable,
|
|
330
|
+
inputsGeneratedWithAi,
|
|
331
|
+
inputsUsingAi,
|
|
332
|
+
|
|
333
|
+
setInputsUsingAi,
|
|
334
|
+
setInputsGeneratedWithAi,
|
|
335
|
+
setSuggestions,
|
|
336
|
+
setCurrentSuggestionValue,
|
|
337
|
+
generateProductSuggestions,
|
|
338
|
+
regenerateProductSuggestions,
|
|
339
|
+
clearCurrentSuggestions,
|
|
340
|
+
|
|
341
|
+
}}
|
|
342
|
+
>
|
|
343
|
+
{ children }
|
|
344
|
+
</AiProductEdition.Provider>
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const STATUS_DICTIONARY = {
|
|
2
|
+
// R: {
|
|
3
|
+
// status: "R",
|
|
4
|
+
// name: "Recepción",
|
|
5
|
+
// weight: 1
|
|
6
|
+
// },
|
|
7
|
+
PA: {
|
|
8
|
+
status: "PA",
|
|
9
|
+
name: "Por Asignar",
|
|
10
|
+
weight: 2
|
|
11
|
+
},
|
|
12
|
+
AS: {
|
|
13
|
+
status: "AS",
|
|
14
|
+
name: "Asignado",
|
|
15
|
+
weight: 3
|
|
16
|
+
},
|
|
17
|
+
// CA: {
|
|
18
|
+
// status: "CA",
|
|
19
|
+
// name: "Capturando",
|
|
20
|
+
// weight: 4
|
|
21
|
+
// },
|
|
22
|
+
// IE: {
|
|
23
|
+
// status: "IE",
|
|
24
|
+
// name: "Información Enviada",
|
|
25
|
+
// weight: 5
|
|
26
|
+
// },
|
|
27
|
+
RCA: {
|
|
28
|
+
status: "RCA",
|
|
29
|
+
name: "Rechazado Cadena",
|
|
30
|
+
weight: 6
|
|
31
|
+
},
|
|
32
|
+
RP: {
|
|
33
|
+
status: "RP",
|
|
34
|
+
name: "Rechazado Proveedor",
|
|
35
|
+
weight: 7
|
|
36
|
+
},
|
|
37
|
+
RA: {
|
|
38
|
+
status: "RA",
|
|
39
|
+
name: "Rechazado Auditor",
|
|
40
|
+
weight: 8
|
|
41
|
+
},
|
|
42
|
+
RC: {
|
|
43
|
+
status: "RC",
|
|
44
|
+
name: "Rechazo Coordinador",
|
|
45
|
+
weight: 9
|
|
46
|
+
},
|
|
47
|
+
AC: {
|
|
48
|
+
status: "AC",
|
|
49
|
+
name: "Aprobado Coordinador",
|
|
50
|
+
weight: 10
|
|
51
|
+
},
|
|
52
|
+
AA: {
|
|
53
|
+
status: "AA",
|
|
54
|
+
name: "Aprobado Auditor",
|
|
55
|
+
weight: 11
|
|
56
|
+
},
|
|
57
|
+
AP: {
|
|
58
|
+
status: "AP",
|
|
59
|
+
name: "Aprobado Proveedor",
|
|
60
|
+
weight: 12
|
|
61
|
+
},
|
|
62
|
+
ACA: {
|
|
63
|
+
status: "ACA",
|
|
64
|
+
name: "Aprobado Cadena",
|
|
65
|
+
weight: 13
|
|
66
|
+
},
|
|
67
|
+
Ex: {
|
|
68
|
+
status: "Ex",
|
|
69
|
+
name: "Exportado",
|
|
70
|
+
weight: 14
|
|
71
|
+
},
|
|
72
|
+
// RAC: {
|
|
73
|
+
// status: "RAC",
|
|
74
|
+
// name: "Rechazo de Auditoría Content-oh!",
|
|
75
|
+
// weight: 15
|
|
76
|
+
// },
|
|
77
|
+
// SAC: {
|
|
78
|
+
// status: "SAC",
|
|
79
|
+
// name: "Solicitud de Auditoría Content-oh!",
|
|
80
|
+
// weight: 16
|
|
81
|
+
// },
|
|
82
|
+
// AAC: {
|
|
83
|
+
// status: "AAC",
|
|
84
|
+
// name: "Aprobado Auditoría Content-oh!",
|
|
85
|
+
// weight: 17
|
|
86
|
+
// },
|
|
87
|
+
// FAP: {
|
|
88
|
+
// status: "FAP",
|
|
89
|
+
// name: "Finalización de Auditoría por Proveedor",
|
|
90
|
+
// weight: 18
|
|
91
|
+
// },
|
|
92
|
+
|
|
93
|
+
//El status NS necesita ser el mayor de todos
|
|
94
|
+
NS: {
|
|
95
|
+
status: "NS",
|
|
96
|
+
name: "No Solicitado",
|
|
97
|
+
weight: 100
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
STATUS_DICTIONARY
|
|
103
|
+
}
|