contentoh-components-library 21.5.94 → 21.5.96

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.
Files changed (70) hide show
  1. package/dist/ai/utils/compare-strings.js +45 -0
  2. package/dist/components/atoms/GeneralButton/styles.js +1 -1
  3. package/dist/components/atoms/GeneralInput/index.js +249 -54
  4. package/dist/components/atoms/GeneralInput/styles.js +7 -3
  5. package/dist/components/atoms/InputFormatter/index.js +223 -68
  6. package/dist/components/atoms/InputFormatter/styles.js +20 -4
  7. package/dist/components/molecules/StatusAsignationInfo/index.js +11 -1
  8. package/dist/components/molecules/TabsMenu/index.js +13 -1
  9. package/dist/components/molecules/TagAndInput/index.js +364 -24
  10. package/dist/components/molecules/TagAndInput/styles.js +2 -2
  11. package/dist/components/organisms/ChangeStatusModal/index.js +531 -0
  12. package/dist/components/organisms/ChangeStatusModal/styles.js +85 -0
  13. package/dist/components/organisms/FullProductNameHeader/index.js +6 -22
  14. package/dist/components/organisms/InputGroup/index.js +22 -18
  15. package/dist/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +150 -337
  16. package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.context.js +15 -15
  17. package/dist/components/pages/ProviderProductEdition/index.js +395 -361
  18. package/dist/components/pages/ProviderProductEdition/utils.js +1 -0
  19. package/dist/components/pages/RetailerProductEdition/RetailerProductEdition.stories.js +125 -211
  20. package/dist/components/pages/RetailerProductEdition/context/provider-product-edition.context.js +59 -260
  21. package/dist/components/pages/RetailerProductEdition/context/reducers/product.js +50 -38
  22. package/dist/components/pages/RetailerProductEdition/index.js +1741 -2239
  23. package/dist/components/pages/RetailerProductEdition/styles.js +4 -2
  24. package/dist/components/pages/RetailerProductEdition/utils.js +251 -2
  25. package/dist/contexts/AiProductEdition.js +244 -160
  26. package/dist/global-files/statusDictionary.js +103 -0
  27. package/package.json +4 -2
  28. package/src/ai/utils/compare-strings.js +45 -0
  29. package/src/assets/images/Icons/arrow.png +0 -0
  30. package/src/assets/images/Icons/cancel.png +0 -0
  31. package/src/assets/images/Icons/ia-icon.png +0 -0
  32. package/src/assets/images/Icons/loading.svg +5 -0
  33. package/src/assets/images/Icons/reload.png +0 -0
  34. package/src/components/atoms/GeneralButton/styles.js +4 -0
  35. package/src/components/atoms/GeneralInput/index.js +241 -60
  36. package/src/components/atoms/GeneralInput/styles.js +81 -0
  37. package/src/components/atoms/InputFormatter/index.js +200 -51
  38. package/src/components/atoms/InputFormatter/styles.js +284 -0
  39. package/src/components/atoms/RetailerSelector/RetailerSelector.stories.js +10 -0
  40. package/src/components/atoms/RetailerSelector/index.js +3 -0
  41. package/src/components/atoms/RetailerSelector/styles.js +0 -0
  42. package/src/components/molecules/StatusAsignationInfo/index.js +9 -1
  43. package/src/components/molecules/TabsMenu/index.js +12 -0
  44. package/src/components/molecules/TagAndInput/index.js +294 -21
  45. package/src/components/molecules/TagAndInput/styles.js +59 -17
  46. package/src/components/organisms/ChangeStatusModal/index.jsx +488 -0
  47. package/src/components/organisms/ChangeStatusModal/styles.js +333 -0
  48. package/src/components/organisms/FullProductNameHeader/index.js +4 -28
  49. package/src/components/organisms/FullTabsMenu/index.js +1 -1
  50. package/src/components/organisms/InputGroup/index.js +12 -4
  51. package/src/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +174 -202
  52. package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx +14 -14
  53. package/src/components/pages/ProviderProductEdition/index.js +497 -437
  54. package/src/components/pages/ProviderProductEdition/utils.js +2 -2
  55. package/src/components/pages/RetailerProductEdition/RetailerProductEdition.stories.js +136 -243
  56. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.context.jsx +575 -0
  57. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.reducer.js +62 -0
  58. package/src/components/pages/RetailerProductEdition/context/reducers/active-state.js +344 -0
  59. package/src/components/pages/RetailerProductEdition/context/reducers/inputs.js +155 -0
  60. package/src/components/pages/RetailerProductEdition/context/reducers/product.js +114 -0
  61. package/src/components/pages/RetailerProductEdition/context/reducers/system.js +60 -0
  62. package/src/components/pages/RetailerProductEdition/index.js +1563 -1717
  63. package/src/components/pages/RetailerProductEdition/index_old.js +1979 -0
  64. package/src/components/pages/RetailerProductEdition/stories/Auditor.stories.js +101 -0
  65. package/src/components/pages/RetailerProductEdition/stories/ImageEditor.stories.js +115 -0
  66. package/src/components/pages/RetailerProductEdition/stories/TextEditor.stories.js +174 -0
  67. package/src/components/pages/RetailerProductEdition/styles.js +67 -2
  68. package/src/components/pages/RetailerProductEdition/utils.js +240 -0
  69. package/src/contexts/AiProductEdition.jsx +347 -0
  70. package/src/global-files/statusDictionary.js +103 -0
@@ -1,392 +1,286 @@
1
- import { Container } from "./styles";
2
- import { HeaderTop } from "../../molecules/HeaderTop";
3
- import { ImagePreviewer } from "../../organisms/ImagePreviewer";
4
- import { ImageDataTable } from "../../organisms/ImageDataTable";
5
- import { FullProductNameHeader } from "../../organisms/FullProductNameHeader";
6
- import { FullTabsMenu } from "../../organisms/FullTabsMenu";
7
- import { InputGroup } from "../../organisms/InputGroup";
8
- import { useEffect, useReducer, useState, useCallback } from "react";
9
- import { GalleryElement } from "../../molecules/GalleryElement";
1
+ import { useEffect, useState } from "react";
2
+
3
+ import axios from "axios";
4
+ import { useDropzone } from "react-dropzone";
10
5
  import { saveAs } from "file-saver";
11
- import { Commentary } from "../../atoms/Commentary";
6
+
12
7
  import {
13
- getRetailerServices,
14
- getPercentage,
15
- fetchUsers,
16
- } from "../../../global-files/data";
17
- import { GalleryHeader } from "../../molecules/GalleryHeader";
18
- import { ProductImageModal } from "../../organisms/ProductImageModal";
19
- import { useDropzone } from "react-dropzone";
20
- import axios from "axios";
21
- import { v4 as uuidv4 } from "uuid";
22
- import AWS from "aws-sdk";
23
- import attributesSent from "../../../assets/images/modalsSVGs/attributesSent.svg";
24
- import descriptionSent from "../../../assets/images/modalsSVGs/descriptionSent.svg";
25
- import imagesSent from "../../../assets/images/modalsSVGs/uploadingImages.svg";
26
- import { TagAndInput } from "../../molecules/TagAndInput/index";
27
- import { Button } from "../../atoms/GeneralButton";
28
- import { GenericModal } from "../../atoms/GenericModal";
29
- import { ScreenHeader } from "../../atoms/ScreenHeader";
30
- import { Loading } from "../../atoms/Loading";
31
- import succes from "../../../assets/images/genericModal/genericModalCheck.svg";
32
- import { VersionSelector } from "../../organisms/VersionSelector";
33
- import { useCloseModal } from "../../../global-files/customHooks";
8
+ ProviderProductEditionProvider,
9
+ useProviderProductEdition,
10
+ } from "./context/provider-product-edition.context";
34
11
  import {
12
+ normalizeProduct,
13
+ getConceptByTab,
35
14
  getAuditVersion,
36
15
  getInputsData,
37
16
  createMessage,
38
17
  sendMessage,
18
+ translateConcept,
19
+ getStatusArrayByRole,
20
+ getConceptsByRole,
21
+ buildCollaboratorAssignations,
22
+ calculateRequiredNull,
39
23
  } from "./utils";
40
- import { Modal } from "../../organisms/Modal";
24
+
25
+ import {
26
+ fetchUsers,
27
+ getPercentage,
28
+ getRetailerServices,
29
+ getServicesData,
30
+ } from "../../../global-files/data";
31
+ import { useCloseModal } from "../../../global-files/customHooks";
32
+
33
+ import { Button } from "../../atoms/GeneralButton";
34
+ import { GenericModal } from "../../atoms/GenericModal";
35
+ import { Loading } from "../../atoms/Loading";
36
+ import { ScreenHeader } from "../../atoms/ScreenHeader";
41
37
  import { ButtonV2 } from "../../atoms/ButtonV2";
42
- import generateThumbnail from "./generateThumnail";
43
-
44
- const reducerImages = (state, action) => {
45
- let { values, attrForImgs, inputsByRetailer } = state;
46
- switch (action.action) {
47
- case "init":
48
- const newInputsByRetailer = {};
49
- action?.init?.inputsByRetailer?.forEach((inputs) => {
50
- inputs?.forEach((input) => {
51
- if (!newInputsByRetailer[`${input.id_retailer}`])
52
- newInputsByRetailer[`${input.id_retailer}`] = [];
53
- newInputsByRetailer[`${input.id_retailer}`].push(input);
54
- });
55
- });
56
- inputsByRetailer = newInputsByRetailer;
57
- return {
58
- ...action.init,
59
- inputsByRetailer,
60
- valuesInitial: action.init.values,
61
- inputsInitial: action.init.inputs,
62
- };
63
- case "addImg":
64
- values = [...values, action.img];
65
- return { ...state, values };
66
- case "changeImageInfo":
67
- values[action.index][action.attribute] = action.value;
68
- return { ...state, values };
69
- case "changeShotType":
70
- values[action.index][action.attribute] = action.value;
71
- return { ...state, values };
72
-
73
- case "changeAttrValue":
74
- const index = attrForImgs.general.findIndex((f) => f.id === action.id);
75
- if (index !== -1) {
76
- attrForImgs.general[index].value = action.value;
77
- }
78
38
 
79
- return { ...state, attrForImgs, values };
80
- case "deleteImage":
81
- values = values.filter(
82
- (value) => action.selectedImages.indexOf(value) === -1
83
- );
84
- return { ...state, values };
85
- case "orderImages": {
86
- let { inputsByRetailer, valuesInitial, inputsInitial, inputs } = state;
87
- try {
88
- const orderedImages = [];
89
- const imageIdArray = [];
90
- if (action?.retailerId && !inputsByRetailer[action.retailerId]) {
91
- inputsByRetailer[action.retailerId] = [];
92
- }
93
- action.retailerId &&
94
- inputsByRetailer[action.retailerId]?.filter((input) => {
95
- imageIdArray.push(input.id_image);
96
- valuesInitial.forEach(async (value) => {
97
- if (value.ext == "mp4") {
98
- convertirVideoABase64(
99
- "https://" +
100
- process.env.REACT_APP_IMAGES_BUCKET +
101
- ".s3.amazonaws.com/" +
102
- value.srcDB
103
- )
104
- .then(async (base64) => {
105
- const videoSrc =
106
- "data:video/mp4;base64," + base64.split(",")[1]; // Añade el tipo MIME adecuado
107
- value.src = await generateThumbnail(videoSrc, 2);
108
- })
109
- .catch((error) => {
110
- console.error("Error:", error);
111
- });
112
- }
113
- if (value.image_id === input.id_image) orderedImages.push(value);
114
- });
115
- });
116
- inputs = inputsInitial?.filter((input) =>
117
- imageIdArray.includes(input.id)
118
- );
39
+ import { GalleryElement } from "../../molecules/GalleryElement";
40
+ import { GalleryHeader } from "../../molecules/GalleryHeader";
41
+ import { HeaderTop } from "../../molecules/HeaderTop";
42
+ import { TagAndInput } from "../../molecules/TagAndInput/index";
119
43
 
120
- values = orderedImages.length > 0 ? orderedImages : [];
121
- } catch (error) {
122
- console.log(error);
123
- }
124
- return { ...state, values, inputs };
125
- }
126
- default:
127
- return state;
128
- }
129
- };
44
+ import { FullProductNameHeader } from "../../organisms/FullProductNameHeader";
45
+ import { FullTabsMenu } from "../../organisms/FullTabsMenu";
46
+ import { ImageDataTable } from "../../organisms/ImageDataTable";
47
+ import { ImagePreviewer } from "../../organisms/ImagePreviewer";
48
+ import { InputGroup } from "../../organisms/InputGroup";
49
+ import { ProductImageModal } from "../../organisms/ProductImageModal";
50
+ import { VersionSelector } from "../../organisms/VersionSelector";
51
+ import { Modal } from "../../organisms/Modal";
130
52
 
131
- const S3_BUCKET = process.env.REACT_APP_IMAGES_BUCKET;
132
- const REGION = "us-east-1";
133
-
134
- function convertirVideoABase64(url) {
135
- return new Promise((resolve, reject) => {
136
- const xhr = new XMLHttpRequest();
137
- xhr.open("GET", url, true);
138
- xhr.responseType = "blob";
139
- xhr.onload = function () {
140
- if (xhr.status === 200) {
141
- const blob = xhr.response;
142
- const reader = new FileReader();
143
- reader.onloadend = function () {
144
- resolve(reader.result);
145
- };
146
- reader.readAsDataURL(blob);
147
- } else {
148
- reject(new Error("Error al cargar el video"));
149
- }
150
- };
151
- xhr.onerror = function () {
152
- reject(new Error("Error de red al cargar el video"));
153
- };
154
- xhr.send();
155
- });
156
- }
157
-
158
- function obtenerDuracionVideoBase64(base64Data) {
159
- return new Promise((resolve, reject) => {
160
- const video = document.createElement("video");
161
- video.src = base64Data;
162
- video.addEventListener("loadedmetadata", () => {
163
- resolve(video.duration);
164
- });
165
- video.addEventListener("error", reject);
166
- });
167
- }
53
+ import successIcon from "../../../assets/images/genericModal/genericModalCheck.svg";
54
+ import errorIcon from "../../../assets/images/genericModal/errorModal.svg";
55
+ import warningIcon from "../../../assets/images/genericModal/genericModalWarning.svg";
56
+ import attributesSent from "../../../assets/images/modalsSVGs/attributesSent.svg";
57
+ import descriptionSent from "../../../assets/images/modalsSVGs/descriptionSent.svg";
58
+ import imagesSent from "../../../assets/images/modalsSVGs/uploadingImages.svg";
168
59
 
169
- AWS.config.update({
170
- accessKeyId: process.env.REACT_APP_KUTS3,
171
- secretAccessKey: process.env.REACT_APP_AKUTS3,
172
- });
60
+ import { Container } from "./styles";
61
+ import { Commentary } from "../../atoms/Commentary";
173
62
 
174
- const myBucket = new AWS.S3({
175
- params: { Bucket: S3_BUCKET },
176
- region: REGION,
177
- });
63
+ import { AiProductEditionProvider } from "../../../contexts/AiProductEdition";
64
+ import ChangeStatusModal from "../../organisms/ChangeStatusModal";
178
65
 
179
- export const RetailerProductEdition = ({
66
+ import { Skeleton, Box, Stack } from "@mui/material";
67
+
68
+ const RetailerProductEditionView = ({
180
69
  tabsSections,
181
70
  productSelected = {},
182
71
  user = {},
183
72
  token,
184
73
  location,
185
74
  }) => {
186
- const [activeTab, setActiveTab] = useState("Descripción");
187
- const [activeImage, setActiveImage] = useState();
188
- const [imageLayout, setImageLayout] = useState(false);
75
+ const {
76
+ state,
77
+ dispatch,
78
+ saveDatasheets,
79
+ saveDescriptions,
80
+ updateImages,
81
+ saveImageAttrs,
82
+ deleteImages,
83
+ handleOnDownloadImages,
84
+ createComment,
85
+ } = useProviderProductEdition();
86
+
87
+ const isRetailer = user?.is_retailer;
88
+
89
+ const [showVersionSelector, setShowVersionSelector] =
90
+ useCloseModal("version-selector");
91
+ const [auditableVersion, setAuditableVersion] = useState(null);
189
92
  const [headerTop, setHeaderTop] = useState(0);
190
- const [descriptions, setDescriptions] = useState([]);
191
- const [datasheets, setDatasheets] = useState([]);
192
- const [images, setImages] = useReducer(reducerImages, {});
193
- const [showModal, setShowModal] = useState(false);
93
+ const [shotThd, setShotThd] = useState(false);
94
+ const [observation, setObservation] = useState();
194
95
  const [showRejectModal, setShowRejectModal] = useState(false);
195
- const { getRootProps, getInputProps } = useDropzone({
196
- accept: "image/*, video/mp4",
96
+ const [isObservationVisible, setObservationVisible] = useState(false);
97
+ const [imageLayout, setImageLayout] = useState(false);
98
+ const [crossComment, setCrossComment] = useState(false);
99
+ const [statusArray, setStatusArray] = useState([]);
100
+ const [rejectAll, setRejectAll] = useState(false);
101
+
102
+ const [auditServices, setAuditServices] = useState([]);
103
+ const [auditDatasheets, setAuditDatasheets] = useState([]);
104
+ const [auditDescriptions, setAuditDescriptions] = useState([]);
105
+ const [showModal, setShowModal] = useState(false);
106
+
107
+ const [compare, setCompare] = useState(false);
108
+ const { getRootProps, getInputProps, isDragActive, open } = useDropzone({
109
+ accept: "image/*",
197
110
  noKeyboard: true,
198
111
  multiple: true,
199
112
  noClick: true,
200
113
  onDrop: (acceptedFiles) => {
201
- const newImages = [];
202
114
  acceptedFiles.map((file) => {
203
115
  const reader = new FileReader();
204
116
  reader.fileName = file.name;
117
+
205
118
  reader.onload = async function (e) {
206
- const ext = e.srcElement.fileName.split(".");
119
+ const fileName = e.target.fileName;
120
+ const fileExtension = fileName.split(".").pop();
121
+ const fileDataURL = e.target.result;
122
+
207
123
  const img = new Image();
208
- if (ext[ext.length - 1] == "mp4") {
209
- obtenerDuracionVideoBase64(e.target.result)
210
- .then(async (duracion) => {
211
- if (duracion > 15 && duracion < 600) {
212
- img.src = await generateThumbnail(e.target.result, 2);
213
- setTimeout(() => {
214
- const width = img.width;
215
- const height = img.height;
216
- const newImg = {
217
- action: "addImg",
218
- img: {
219
- src: img.src, //e.target.result,
220
- name: e.target.fileName,
221
- ext: ext[ext.length - 1],
222
- width: width,
223
- height: height,
224
- video_src:
225
- ext[ext.length - 1] == "mp4" ? e.target.result : "",
226
- },
227
- };
228
- setImages(newImg);
229
- }, 500);
230
- } else {
231
- setModalAlert({
232
- show: true,
233
- title: "Hubo un error al subir el video",
234
- message:
235
- "Los videos deben durar entre 15 segundos y 10 minutos",
236
- });
237
- }
238
- })
239
- .catch((error) => {
240
- console.error("Error al obtener la duración del video:", error);
241
- });
242
- } else {
243
- img.src = e.target.result;
244
- setTimeout(() => {
245
- const width = img.width;
246
- const height = img.height;
247
- const newImg = {
248
- action: "addImg",
249
- img: {
250
- src: img.src, //e.target.result,
251
- name: e.target.fileName,
252
- ext: ext[ext.length - 1],
253
- width: width,
254
- height: height,
255
- video_src:
256
- ext[ext.length - 1] == "mp4" ? e.target.result : "",
257
- },
258
- };
259
- setImages(newImg);
260
- }, 500);
261
- }
124
+ img.src = fileDataURL;
125
+
126
+ img.onload = function () {
127
+ const width = img.width;
128
+ const height = img.height;
129
+ const newImg = {
130
+ src: fileDataURL,
131
+ name: fileName,
132
+ ext: fileExtension,
133
+ width: width,
134
+ height: height,
135
+ isApproved: true, // VALIDAR QUE SOLO SEA PARA RADIOSHACK
136
+ };
137
+
138
+ dispatch({
139
+ type: "ADD_IMAGE_VALUE",
140
+ payload: newImg,
141
+ });
142
+ };
262
143
  };
144
+
263
145
  reader.onerror = function (error) {
264
146
  console.log("dropzoneError: ", error);
265
147
  };
148
+
266
149
  reader.readAsDataURL(file);
267
150
  return file;
268
151
  });
269
152
  },
270
153
  });
271
- const [updatedDatasheets, setUpdatedDatasheets] = useState([]);
272
- const [updatedDescriptions, setUpdatedDescriptions] = useState([]);
273
- const [imagesUploaded, setImagesUploaded] = useState(false);
274
- const [dataImages, setDataImages] = useState();
275
- const [percentages, setPercentages] = useState(
276
- new Array(product?.retailers?.length).fill({ percentage: 0 })
277
- );
278
- // const [percentages, setPercentages] = useState([{ retailers: {} }]);
279
- const [activePercentage, setActivePercentage] = useState(0);
280
- const [activeRetailer, setActiveRetailer] = useState({});
281
- const [services, setServices] = useState([]);
282
- const [servicesData, setServicesData] = useState([]);
283
- const [message, setMessage] = useState("");
284
- const [product, setProduct] = useState(
285
- JSON.parse(sessionStorage.getItem("productSelected"))
286
- ? JSON.parse(sessionStorage.getItem("productSelected"))
287
- : productSelected
288
- );
289
- const [icon, setIcon] = useState(null);
290
- const [version, setVersion] = useState(product?.version);
291
- const [comments, setComments] = useState({});
292
- const [comment, setComment] = useState("");
293
- const [requiredNull, setRequiredNull] = useState({
294
- "Ficha técnica": 0,
295
- Descripción: 0,
296
- Imágenes: 0,
297
- });
298
- const [crossComment, setCrossComment] = useState(false);
299
- const [userGroups, setUserGroups] = useState([]);
300
- const [assig, setAssig] = useState({});
301
- const [selectedImages, setSelectedImages] = useState([]);
302
- const [componentsArray, setComponentsArray] = useState([]);
303
- const [checkAll, setCheckAll] = useState(false);
304
- const isRetailer = user?.is_retailer;
305
- const [loading, setLoading] = useState(true);
306
- const [retailerStatus, setRetailerStatus] = useState("-");
307
- const [statusArray, setStatusArray] = useState([]);
308
- const [socketType, setSocketType] = useState(null);
309
- const [saving, setSaving] = useState(loading);
310
- const [showVersionSelector, setShowVersionSelector] =
311
- useCloseModal("version-selector");
312
- const shotThd = [58, 59, 60, 61].includes(activeRetailer.id);
313
- const [auditableVersion, setAuditableVersion] = useState(null);
314
- const [auditServices, setAuditServices] = useState([]);
315
- const [auditDatasheets, setAuditDatasheets] = useState([]);
316
- const [auditDescriptions, setAuditDescriptions] = useState([]);
317
- const [auditImages, setAuditImages] = useState([]);
318
- const [compare, setCompare] = useState(false);
319
- const [observation, setObservation] = useState();
320
- const [valRejAll, setValRejAll] = useState(false);
321
- const [modalAlert, setModalAlert] = useState({
322
- show: false,
323
- title: "",
324
- message: "",
325
- errorInputMessage: false,
326
- });
327
154
 
328
- const [desc, setDesc] = useState([]);
329
- const [fich, setFich] = useState([]);
330
- const [imag, setImag] = useState([]);
155
+ const [openChangeStatusModal, setOpenChangeStatusModal] = useState(false);
331
156
 
332
- const [isObservationVisible, setObservationVisible] = useState(false);
157
+ const thumbs = () => {
158
+ const imageInputs = state.images_values?.inputs?.map((e) => ({
159
+ value: e?.id,
160
+ name: e?.name,
161
+ required: e?.required,
162
+ active: state.images_values?.values?.some(
163
+ (value) => value?.image_id === e?.id,
164
+ ),
165
+ }));
166
+ const imageType = state.images_values?.imageType?.map((e) => ({
167
+ value: e?.id,
168
+ name: e?.name,
169
+ }));
170
+ const imagePackagingType = state.images_values?.imagePackagingType?.map(
171
+ (e) => ({
172
+ value: e?.id,
173
+ name: e?.name,
174
+ }),
175
+ );
333
176
 
334
- const toggleObservation = () => {
335
- setObservationVisible(!isObservationVisible);
336
- };
337
- // const handleClickOutside = (event) => {
338
- // if (
339
- // isObservationVisible &&
340
- // !event.target.closest(".Observation") &&
341
- // !event.target.closest(".Container")
342
- // ) {
343
- // hideObservation();
344
- // }
345
- // };
346
-
347
- // useEffect(() => {
348
- // document.addEventListener("click", handleClickOutside);
349
- // return () => {
350
- // document.removeEventListener("click", handleClickOutside);
351
- // };
352
- // }, [isObservationVisible]);
353
-
354
- const hideObservation = () => {
355
- setObservationVisible(false);
177
+ return state.images_values?.values?.map((image, index) => (
178
+ <GalleryElement
179
+ id={"gallery-element-" + index}
180
+ index={index + "-" + image.name}
181
+ key={index}
182
+ image={image}
183
+ number={index}
184
+ gridLayout={imageLayout}
185
+ imageType={imageType}
186
+ imagePackagingType={imagePackagingType}
187
+ imageInputs={imageInputs}
188
+ selectedImages={state.selected_images}
189
+ setCheckAll={handleOnSetCheckAll}
190
+ changeImage={(item) => {
191
+ dispatch({ type: "UPDATE_IMAGE_VALUE", payload: item });
192
+ }}
193
+ setSelectedImages={(item) =>
194
+ dispatch({ type: "SET_SELECTED_IMAGE", payload: item })
195
+ }
196
+ updateApprovedInputsImages={(item) =>
197
+ console.log("updateApprovedInputsImages", item)
198
+ }
199
+ />
200
+ ));
356
201
  };
357
202
 
358
- const handleCommentSubmit = (e) => {
359
- e.preventDefault();
360
- const commentText = document.querySelector(
361
- "#commentary-box .ql-container .ql-editor > p"
362
- ).innerHTML;
363
- createComment(e, commentText, activeTab);
203
+ const initializeProduct = async () => {
204
+ try {
205
+ const product = productSelected ? productSelected : JSON.parse(sessionStorage.getItem("productSelected"));
206
+
207
+ const productNormalized = normalizeProduct(product);
208
+
209
+ console.log({productNormalized});
210
+
211
+ dispatch({
212
+ type: "SET_PRODUCT",
213
+ payload: productNormalized,
214
+ });
215
+ dispatch({
216
+ type: "SET_ACTIVE_RETAILER",
217
+ payload: productNormalized.categoryRetailerInOrder[0],
218
+ });
219
+ setShotThd(
220
+ [58, 59, 60, 61].includes(
221
+ productNormalized.categoryRetailerInOrder[0].id_retailer,
222
+ ),
223
+ );
224
+
225
+ // Configurar statusArray según el rol del usuario
226
+ setStatusArray(getStatusArrayByRole(user.id_role));
227
+
228
+ // Cargar usuarios y asignaciones
229
+ const users = await fetchUsers(token);
230
+ if (users.length > 0) {
231
+ dispatch({
232
+ type: "SET_COLLABORATOR_ASSIGNATIONS",
233
+ payload: buildCollaboratorAssignations(productNormalized, users),
234
+ });
235
+ }
236
+
237
+ const { id_article } = productNormalized;
238
+ if (id_article) {
239
+ await getAuditVersion(id_article, setAuditableVersion, token);
240
+ }
241
+
242
+ if (id_article && productNormalized.id_order) {
243
+ const response = await axios.get(
244
+ process.env.REACT_APP_READ_OBSERVATION,
245
+ {
246
+ params: {
247
+ articleId: id_article,
248
+ orderId: productNormalized.id_order,
249
+ },
250
+ headers: {
251
+ Authorization: sessionStorage.getItem("jwt"),
252
+ },
253
+ },
254
+ );
255
+ const parseData = JSON.parse(response.data.body).data[0];
256
+ setObservation(parseData.observations);
257
+ }
258
+ } catch (error) {
259
+ console.log("Error setting product data: ", error);
260
+ }
364
261
  };
365
262
 
366
- useEffect(async () => {
367
- const { id_article } = product?.article || {};
368
- if (id_article)
369
- await getAuditVersion(id_article, setAuditableVersion, token);
263
+ // Setter del producto y carga inicial (optimizado para evitar cascada)
264
+ useEffect(() => {
265
+ initializeProduct();
370
266
  }, []);
371
267
 
372
268
  const loadAuditableData = async () => {
373
- if (auditableVersion) {
374
- const auditServices = await getRetailerServices(
375
- product?.article?.id_article,
376
- product?.article?.category,
377
- parseInt(product?.article?.id_category),
378
- auditableVersion.version,
379
- token
380
- );
381
- getInputsData(
382
- auditServices,
383
- activeRetailer,
384
- setAuditDatasheets,
385
- setAuditDescriptions
386
- );
387
- setAuditServices(auditServices);
388
- setAuditImages(auditServices[2]);
389
- }
269
+ const auditServices = await getRetailerServices(
270
+ state.product?.id_article,
271
+ state.product?.categoryName,
272
+ parseInt(state.product?.id_category),
273
+ auditableVersion.version,
274
+ token,
275
+ );
276
+ getInputsData(
277
+ auditServices,
278
+ { id: state.active_retailer.id_retailer },
279
+ setAuditDatasheets,
280
+ setAuditDescriptions,
281
+ );
282
+ setAuditServices(auditServices);
283
+ setAuditImages(auditServices[2]);
390
284
  };
391
285
 
392
286
  useEffect(() => {
@@ -395,682 +289,438 @@ export const RetailerProductEdition = ({
395
289
  }
396
290
  }, [auditableVersion]);
397
291
 
398
- useEffect(() => {
399
- checkAll && setSelectedImages(images.values);
400
- }, [checkAll]);
401
-
402
- const loadData = async () => {
292
+ const loadData = async (enableLoading = true, versionSelected = null) => {
403
293
  try {
404
- setLoading(true);
405
-
406
- const ids =
407
- product?.statusByRetailer?.map((item) => item.retailer_id) ??
408
- product?.statusByRetailer?.map((item) => item.id_retailer) ??
409
- [];
410
- const uniqueIds = [...new Set(ids)];
411
-
412
- let activeRetailerId = activeRetailer?.id;
413
- if (!activeRetailerId) {
414
- const firstValid = product?.article?.categoryRetailer?.find((item) =>
415
- uniqueIds.includes(item.id_retailer || item.retailer_id)
416
- );
417
- if (firstValid) {
418
- activeRetailerId = firstValid.id_retailer || firstValid.retailer_id;
419
- }
420
- }
421
-
422
- const categoryRetailer =
423
- product?.article?.categoryRetailer?.filter(
424
- (item) =>
425
- uniqueIds.includes(item.id_retailer) &&
426
- item.id_retailer === activeRetailerId
427
- ) ??
428
- product?.article?.categoryRetailer?.filter(
429
- (item) =>
430
- uniqueIds.includes(item.retailer_id) &&
431
- item.retailer_id === activeRetailerId
432
- ) ??
433
- [];
434
-
435
- const categoryName = categoryRetailer[0]?.category_name;
436
- const categoryId = categoryRetailer[0]?.id_category;
437
-
438
- const category_name =
439
- categoryName ??
440
- (product?.article?.company_name || product?.categoryName);
441
- const category_id =
442
- categoryId ??
443
- parseInt(product?.article?.id_category || product?.id_category);
444
-
445
- const retailer_id = activeRetailerId ?? categoryRetailer?.id_retailer;
446
-
447
- const services = await getRetailerServices(
448
- product?.id_article || product?.article?.id_article,
449
- category_name,
450
- category_id,
451
- version,
452
- token,
453
- retailer_id
454
- );
455
-
456
- if (auditableVersion && retailer_id) {
457
- loadAuditableData();
458
- }
459
-
460
- setServices(services);
461
- const generalServices = await getServices();
462
-
463
- setImages({ action: "init", init: services[2] });
294
+ if (enableLoading) dispatch({ type: "SET_LOADING", payload: true });
464
295
 
465
- if (services[2]?.values?.length > 0) setActiveImage(0);
296
+ const { id_article, version, id_order } = state.product;
297
+ const { category, id_category, id_retailer } = state.active_retailer;
466
298
 
299
+ // Payloads y headers
467
300
  const data = [
468
301
  {
469
- id_article: product?.article?.id_article,
302
+ id_article,
303
+ version: versionSelected ? versionSelected : version,
470
304
  relations: [
471
305
  {
472
- id_retailer: retailer_id,
473
- id_category: category_id,
306
+ id_retailer,
307
+ id_category,
474
308
  },
475
309
  ],
476
- version: product.version,
477
310
  },
478
311
  ];
479
312
 
480
313
  const headers = { Authorization: token };
481
- getPercentage({ data, headers }).then((res) => {
482
- setPercentages(JSON.parse(res?.[0]?.body));
314
+
315
+ const [services, percentagesRes, servicesDataRes, commentsResponse] =
316
+ await Promise.all([
317
+ getRetailerServices(
318
+ id_article,
319
+ category,
320
+ id_category,
321
+ versionSelected ?? version,
322
+ token,
323
+ id_retailer,
324
+ ),
325
+ getPercentage({ data, headers }),
326
+ getServicesData({
327
+ articleId: id_article,
328
+ orderId: id_order,
329
+ end: true,
330
+ token,
331
+ }),
332
+ // Obtener comentarios en paralelo
333
+ Promise.all([
334
+ axios.get(process.env.REACT_APP_COMMENTS_ENDPOINT, {
335
+ params: {
336
+ articleId: id_article,
337
+ concept: "description",
338
+ orderIdColab: id_order,
339
+ version: versionSelected ?? version,
340
+ },
341
+ }),
342
+ axios.get(process.env.REACT_APP_COMMENTS_ENDPOINT, {
343
+ params: {
344
+ articleId: id_article,
345
+ concept: "datasheet",
346
+ orderIdColab: id_order,
347
+ version: versionSelected ?? version,
348
+ },
349
+ }),
350
+ axios.get(process.env.REACT_APP_COMMENTS_ENDPOINT, {
351
+ params: {
352
+ articleId: id_article,
353
+ concept: "images",
354
+ orderIdColab: id_order,
355
+ version: versionSelected ?? version,
356
+ },
357
+ }),
358
+ ]),
359
+ ]);
360
+
361
+ dispatch({
362
+ type: "SET_RETAILER_STATUS",
363
+ payload: servicesDataRes.map((service) => ({
364
+ retailer_id: service?.id_retailer,
365
+ service: service?.service,
366
+ status: service?.status,
367
+ task_user_group_id: service?.task_user_group_id,
368
+ })),
483
369
  });
484
370
 
485
- setImages({ action: "orderImages", retailerId: activeRetailer.id });
371
+ // Extraer relaciones del response
372
+ const percentages =
373
+ JSON.parse(percentagesRes?.[0]?.body)?.[0]?.relations ?? [];
374
+
375
+ // Procesar comentarios
376
+ let commentsMap = {};
377
+ commentsResponse.forEach((commentRes) => {
378
+ const commentData = JSON.parse(commentRes?.data?.body).data[0];
379
+ if (commentData) {
380
+ const tab = translateConcept(commentData.concept);
381
+ commentsMap[tab] = commentData;
382
+ }
383
+ });
384
+ dispatch({ type: "SET_COMMENTS", payload: commentsMap });
385
+
386
+ // Ordenamiento de imágenes
387
+ const orderMap = services[2].inputsByRetailer
388
+ .flat()
389
+ .reduce((acc, item) => {
390
+ acc[item.id_image] = item.order;
391
+ return acc;
392
+ }, {});
393
+
394
+ const orderedValues = [...services[2].values].sort((a, b) => {
395
+ const orderA = orderMap[a.image_id] ?? Number.MAX_SAFE_INTEGER;
396
+ const orderB = orderMap[b.image_id] ?? Number.MAX_SAFE_INTEGER;
397
+ return orderA - orderB;
398
+ });
486
399
 
487
- setLoading(false);
400
+ // Procesamiento de inputs del retailer activo (evita cascada con state.services)
401
+ const retailerDatasheets = services[0][id_retailer];
402
+ const datasheetsActiveRetailer = {
403
+ ...retailerDatasheets,
404
+ data: Object.values(retailerDatasheets.data),
405
+ };
406
+
407
+ const descriptionsActiveRetailer = services[1].filter(
408
+ (item) => item.id === id_retailer,
409
+ );
410
+
411
+ const filteredValues = services[2].values.filter((value) => {
412
+ return services[2].inputsByRetailer.some((retailerInput) =>
413
+ retailerInput.some(
414
+ (input) =>
415
+ input.id_retailer === id_retailer &&
416
+ input.id_image === value.image_id,
417
+ ),
418
+ );
419
+ });
420
+
421
+ // Dispatch consolidado
422
+ dispatch({
423
+ type: "SET_SERVICES",
424
+ payload: {
425
+ datasheets: services[0],
426
+ descriptions: services[1],
427
+ images: {
428
+ ...services[2],
429
+ values: orderedValues,
430
+ },
431
+ },
432
+ });
433
+
434
+ dispatch({ type: "SET_PERCENTAGES", payload: percentages });
435
+ dispatch({
436
+ type: "SET_ACTIVE_PERCENTAGES",
437
+ payload: percentages.find(
438
+ ({ id_retailer: rId }) => rId === id_retailer,
439
+ ),
440
+ });
441
+ dispatch({ type: "SET_SERVICES_DATA", payload: servicesDataRes });
442
+
443
+ // Setear inputs del retailer activo directamente (evita cascada)
444
+ dispatch({
445
+ type: "SET_DATASHEETS_INPUTS",
446
+ payload: [datasheetsActiveRetailer, services[0].inputs],
447
+ });
448
+
449
+ dispatch({
450
+ type: "SET_DESCRIPTIONS_INPUTS",
451
+ payload: descriptionsActiveRetailer,
452
+ });
453
+
454
+ dispatch({
455
+ type: "SET_IMAGES_VALUES",
456
+ payload: {
457
+ ...services[2],
458
+ values: filteredValues,
459
+ },
460
+ });
461
+
462
+ // Calcular campos requeridos sin valor
463
+ dispatch({
464
+ type: "SET_MISSING_REQUIRED_FIELDS",
465
+ payload: calculateRequiredNull(services, servicesDataRes, id_retailer),
466
+ });
467
+
468
+ // Auditable
469
+ if (auditableVersion && id_retailer) {
470
+ loadAuditableData();
471
+ }
488
472
  } catch (error) {
489
- setLoading(false);
490
- console.log(error);
473
+ console.log("Error loading data: ", error);
474
+ dispatch({ type: "SET_ERRORS", payload: [error.message] });
475
+ } finally {
476
+ dispatch({ type: "SET_LOADING", payload: false });
491
477
  }
492
478
  };
493
479
 
494
480
  useEffect(() => {
495
- setLoading(true);
496
- loadData();
497
- }, [activeRetailer]);
498
-
499
- const getServices = async () => {
500
- const servicesResponse = await axios.get(
501
- `${process.env.REACT_APP_SERVICES_ENDPOINT}?articleId=${
502
- product?.article?.id_article
503
- }&orderId=${product?.article?.id_order || product.orderId}&end=true`
504
- );
505
- const parsedResponse = JSON.parse(servicesResponse?.data?.body).data;
481
+ if (state.active_retailer) {
482
+ loadData();
483
+ }
484
+ }, [state.active_retailer]);
506
485
 
507
- const retailers = product.retailers || product.retailersAvailable;
508
- const retailerResponse = parsedResponse?.map((srv) => srv.id_retailer);
509
- const active = retailers?.find((retailer) =>
510
- retailerResponse.includes(retailer.id)
511
- );
512
- !activeRetailer.id && setActiveRetailer(active ? active : retailers[0]);
513
- setServicesData(parsedResponse);
486
+ const handleOnChangeCurrentImage = (index) => {
487
+ if (typeof index === "number") {
488
+ dispatch({ type: "SET_CURRENT_IMAGE", payload: index });
489
+ } else if (typeof index === "object") {
490
+ dispatch({ type: "CHANGE_ATTR_VALUE", payload: index });
491
+ }
514
492
  };
515
493
 
516
- const translateConcept = (concept) => {
517
- const translation = {
518
- datasheet: "Ficha técnica",
519
- description: "Descripción",
520
- images: "Imágenes",
521
- };
522
- return translation[concept];
494
+ const handleOnShowModalGallery = () => {
495
+ dispatch({
496
+ type: "SET_ACTIVE_TAB",
497
+ payload: "Imágenes",
498
+ });
499
+ setShowModal(true);
523
500
  };
524
501
 
525
- const getComments = async (tab = "Descripción") => {
526
- const commentsResponse = await Promise.all([
527
- axios.get(
528
- `${process.env.REACT_APP_COMMENTS_ENDPOINT}?articleId=${product?.article?.id_article}&concept=description&orderIdColab=${product?.orderId}&version=${version}`
529
- ),
530
- axios.get(
531
- `${process.env.REACT_APP_COMMENTS_ENDPOINT}?articleId=${product?.article?.id_article}&concept=datasheet&orderIdColab=${product?.orderId}&version=${version}`
532
- ),
533
- axios.get(
534
- `${process.env.REACT_APP_COMMENTS_ENDPOINT}?articleId=${product?.article?.id_article}&concept=images&orderIdColab=${product?.orderId}&version=${version}`
535
- ),
536
- ]);
537
-
538
- let comments = {};
539
- commentsResponse.forEach(
540
- (comment) =>
541
- JSON.parse(comment?.data?.body).data[0] &&
542
- (comments[
543
- translateConcept(JSON.parse(comment?.data?.body)?.data[0]?.concept)
544
- ] = JSON.parse(comment?.data?.body).data[0])
545
- );
546
- setComment(comments[tab]);
547
- setComments(comments);
502
+ const handleOnChangeActiveRetailer = (retailer) => {
503
+ dispatch({
504
+ type: "SET_ACTIVE_RETAILER",
505
+ payload: retailer,
506
+ });
548
507
  };
549
508
 
550
- useEffect(async () => {
551
- setLoading(true);
552
- // loadData();
553
- getComments();
554
- setUserGroups(await fetchUsers(token));
555
- let arr = [];
556
- switch (user.id_role) {
557
- case 7:
558
- case 8:
559
- arr = ["PA", "AS", "CA", "RC", "RA", "RP", "RCA"];
560
- break;
561
- case 4:
562
- case 5:
563
- arr = ["RC", "AC", "AA", "AP", "ACA"];
564
- break;
565
- case 6:
566
- arr = ["RP", "RCA", "AC", "RA"];
567
- break;
568
- default:
569
- arr = [];
570
- break;
571
- }
572
- setStatusArray(arr);
573
- setLoading(false);
574
- }, [product, version]);
575
-
576
- const loadAssignations = (currentProduct) => {
577
- setAssig({
578
- Descripción: {
579
- assignations: [
580
- {
581
- collaboratorType: "especialist",
582
- id: currentProduct?.article?.id_description_especialist,
583
- },
584
- {
585
- collaboratorType: "facilitator",
586
- id: currentProduct?.article?.id_description_facilitator,
587
- },
588
- ],
589
- collaborators: {
590
- especialist: userGroups[0] || [],
591
- facilitator: userGroups[2] || [],
592
- },
593
- },
594
- "Ficha técnica": {
595
- assignations: [
596
- {
597
- collaboratorType: "especialist",
598
- id: currentProduct?.article?.id_datasheet_especialist,
599
- },
600
- {
601
- collaboratorType: "facilitator",
602
- id: currentProduct?.article?.id_datasheet_facilitator,
603
- },
604
- ],
605
- collaborators: {
606
- especialist: userGroups[0] || [],
607
- facilitator: userGroups[2] || [],
608
- },
609
- },
610
- Imágenes: {
611
- assignations: [
612
- {
613
- collaboratorType: "especialist",
614
- id: currentProduct?.article?.id_images_especialist,
615
- },
616
- {
617
- collaboratorType: "facilitator",
618
- id: currentProduct?.article?.id_images_facilitator,
619
- },
620
- ],
621
- collaborators: {
622
- especialist: userGroups[1] || [],
623
- facilitator: userGroups[3] || [],
624
- },
625
- },
509
+ const handleOnChangeActiveTab = (tab) => {
510
+ dispatch({
511
+ type: "SET_ACTIVE_TAB",
512
+ payload: tab,
626
513
  });
627
514
  };
628
515
 
629
- useEffect(() => {
630
- loadAssignations(product);
631
- }, [userGroups]);
632
-
633
- useEffect(() => {
634
- const retailerPercentages =
635
- percentages?.[0]?.relations?.[0]?.percentagesGeneral;
636
-
637
- const requiredPercentage = retailerPercentages?.required;
638
-
639
- setActivePercentage(requiredPercentage);
640
-
641
- // const retailers = product?.retailersAvailable || product?.retailers;
642
- // if (
643
- // Object.keys(percentages[product.article.id_article] ?? {})?.length > 0
644
- // ) {
645
- // retailers?.forEach((retailer, index) => {
646
- // retailer["percentage"] = Number(
647
- // percentages[product.article.id_article][retailer.id]
648
- // ?.percentageRequired
649
- // );
650
- // });
651
- // }
652
- // setActivePercentage(retailers[0]?.percentage);
653
- }, [percentages]);
654
-
655
- useEffect(() => {
656
- getInputsData(services, activeRetailer, setDatasheets, setDescriptions);
657
- auditableVersion && loadAuditableData();
658
- setActivePercentage(Math.round(activeRetailer?.percentage, 0));
659
- activeRetailer?.id &&
660
- setImages({ action: "orderImages", retailerId: activeRetailer.id });
661
- }, [activeRetailer, services]);
662
-
663
- const thumbs = () => {
664
- const imageInputs = socketType?.slice();
665
- const imageType = images?.imageType?.map((e) => ({
666
- value: e?.id,
667
- name: e?.name,
668
- }));
669
- const imagePackagingType = images?.imagePackagingType?.map((e) => ({
670
- value: e?.id,
671
- name: e?.name,
672
- }));
673
- const imageShotType = images?.imageShotType?.map((e) => ({
674
- value: e?.id,
675
- name: e?.type_shot,
676
- }));
677
- return images?.values?.map((image, index) => (
678
- <GalleryElement
679
- setCheckAll={setCheckAll}
680
- key={index + "-" + image.name}
681
- image={image}
682
- gridLayout={imageLayout}
683
- id={"gallery-element-" + index}
684
- index={index + "-" + image.name + "-" + compare}
685
- number={index}
686
- imageType={imageType}
687
- imagePackagingType={imagePackagingType}
688
- imageInputs={imageInputs}
689
- imageShotType={imageShotType}
690
- setSocketType={setSocketType}
691
- changeImage={setImages}
692
- selectedImages={selectedImages}
693
- setSelectedImages={setSelectedImages}
694
- auditImages={auditImages}
695
- compare={compare}
696
- />
697
- ));
516
+ const handleOnToggleImageLayout = () => {
517
+ setImageLayout(!imageLayout);
698
518
  };
699
519
 
700
- const saveDescriptions = async () => {
701
- const dataClean = updatedDescriptions.filter((f) => f.value !== "");
702
- if (dataClean.length > 0) {
703
- setLoading(true);
704
- const productTemp = product;
705
- const articleId = product?.article?.id_article;
706
- const dataObject = {
707
- articleId,
708
- articleData: dataClean,
709
- };
710
- if (product?.orderId) dataObject["orderId"] = product?.orderId;
711
- try {
712
- const res = await axios.put(
713
- `${process.env.REACT_APP_ARTICLE_DATA_ENDPOINT}?description=true&version=${version}`,
714
- dataObject,
715
- {
716
- headers: {
717
- Authorization: token,
718
- },
719
- }
720
- );
721
- if (res.data.statusCode === 200) {
722
- const { newStatus, newArticleStatus } = JSON.parse(res.data.body);
723
- if (newArticleStatus)
724
- productTemp.status = newArticleStatus[articleId];
725
- if (newStatus) productTemp.description_status = newStatus;
726
- setProduct(productTemp);
727
- sessionStorage.setItem(
728
- "productSelected",
729
- JSON.stringify(productTemp)
730
- );
520
+ const handleOnToggleObservation = () => {
521
+ setObservationVisible(!isObservationVisible);
522
+ };
731
523
 
732
- setUpdatedDescriptions([]);
733
- setMessage("Descripciones guardadas con éxito");
734
- await loadData();
735
- }
736
- } catch (error) {
737
- console.log(error);
738
- } finally {
739
- setLoading(false);
740
- }
741
- }
524
+ const handleOnHideObservation = () => {
525
+ setObservationVisible(false);
742
526
  };
743
527
 
744
- const saveDatasheets = async () => {
745
- const dataClean = updatedDatasheets.filter((f) => f.value !== "");
746
- if (dataClean.length > 0) {
747
- setLoading(true);
748
- const productTemp = product;
749
- const articleId = product?.article?.id_article;
750
- const dataObject = {
751
- articleId,
752
- articleData: dataClean,
753
- };
754
- if (product?.orderId) dataObject["orderId"] = product?.orderId;
755
- try {
756
- const res = await axios.put(
757
- `${process.env.REACT_APP_ARTICLE_DATA_ENDPOINT}?datasheet=true&version=${version}`,
758
- dataObject,
759
- {
760
- headers: {
761
- Authorization: token,
762
- },
763
- }
764
- );
765
- if (res.data.statusCode === 200) {
766
- const { newStatus, newArticleStatus } = JSON.parse(res.data.body);
767
- if (newArticleStatus)
768
- productTemp.status = newArticleStatus[articleId];
769
- if (newStatus) productTemp.datasheet_status = newStatus;
770
- setProduct(productTemp);
771
- sessionStorage.setItem(
772
- "productSelected",
773
- JSON.stringify(productTemp)
774
- );
775
- setUpdatedDatasheets([]);
776
- setMessage("Fichas técnicas guardadas");
777
- await loadData();
778
- }
779
- } catch (error) {
780
- console.log(error);
781
- } finally {
782
- setLoading(false);
783
- }
784
- }
528
+ const isAuditorAssigned = () => {
529
+ return state.product?.id_auditor === user?.id_user;
785
530
  };
786
531
 
787
- useEffect(() => {
788
- const imageInputs = images?.inputs?.map((e) => ({
789
- value: e?.id,
790
- name: e?.name,
791
- required: e?.required,
792
- active: images?.values?.some((value) => value?.image_id === e?.id),
793
- }));
794
- setSocketType(imageInputs);
795
- }, [images]);
796
-
797
- const updateImages = useCallback(async () => {
798
- const imagesList = images?.values?.slice();
799
- const imagesListTemp = imagesList?.reduce((acc, image) => {
800
- acc[image?.image_id] = ++acc[image?.image_id] || 0;
801
- return acc;
802
- }, {});
803
-
804
- const duplicated = imagesList?.filter((image) => {
805
- return imagesListTemp[image?.image_id];
806
- });
532
+ const isUserAssignedToService = (tab, rol) => {
533
+ const concept = getConceptByTab(state.active_tab);
807
534
 
808
- const attrForImgs = Object.values(images?.attrForImgs ?? {}).pop();
809
- const data = {
810
- articleId: product?.article?.id_article,
811
- attrReqImgs: attrForImgs?.map((e) => ({
812
- attrId: e.id,
813
- value: e.value ?? "-",
814
- })),
815
- articleData: imagesList?.filter((e) => !e.id),
816
- updateImages: imagesList?.filter((e) => e.id),
817
- };
818
- if (product?.orderId) data["orderId"] = product?.orderId;
819
- let valid =
820
- data?.articleData?.length === 0
821
- ? true
822
- : data?.articleData?.every((e, i) => {
823
- // if (e?.image_id && e?.packing_type && e?.image_type) {
824
- if (e?.image_id) {
825
- return true;
826
- }
827
- return false;
828
- });
829
- if (valid && data?.updateImages?.length > 0 && duplicated?.length === 0) {
830
- valid = data?.updateImages?.every((e, i) => {
831
- // if (e?.image_id && e?.packing_type && e?.image_type) {
832
- if (e?.image_id) {
833
- return true;
834
- }
835
- return false;
836
- });
837
- }
838
- if (valid && duplicated?.length === 0) {
839
- setLoading(true);
840
- try {
841
- data?.articleData?.forEach((e) => {
842
- e.uuid = uuidv4();
843
- });
844
- setDataImages(data);
845
- if (data?.articleData?.length > 0) {
846
- setImagesUploaded(false);
847
- const promiseArray = [];
848
- data?.articleData?.forEach((e) => {
849
- let file;
850
- if (e.ext == "mp4") {
851
- file = Buffer.from(
852
- e.video_src.replace(/^data:video\/\w+;base64,/, ""),
853
- "base64"
854
- );
855
- e.video_src = "";
856
- } else {
857
- file = Buffer.from(
858
- e.src.replace(/^data:image\/\w+;base64,/, ""),
859
- "base64"
860
- );
861
- }
862
- const params = {
863
- ACL: "public-read",
864
- Body: file,
865
- Bucket: S3_BUCKET,
866
- Key: `id-${data.articleId}/${version}/${e?.image_id}-${e?.uuid}.${e?.ext}`,
867
- };
868
- promiseArray.push(myBucket.putObject(params).promise());
869
- });
870
- await Promise.all(promiseArray);
871
- setImagesUploaded(true);
872
- } else {
873
- setImagesUploaded(true);
874
- }
875
- } catch (err) {
876
- console.log(err);
877
- // setMainLoading(false);
878
- } finally {
879
- setLoading(false);
880
- }
881
- } else {
882
- // setMainLoading(false);
883
- setMessage(
884
- "Completa los campos que solicita cada una de la imágenes o hay imágenes con el mismo tipo de toma.\nRecuerda hay campos obligatorios y no podras avanzar si no estan completos."
885
- );
886
- }
887
- // eslint-disable-next-line react-hooks/exhaustive-deps
888
- }, [images, imagesUploaded]);
889
-
890
- useEffect(async () => {
891
- if (imagesUploaded) {
892
- dataImages.articleData = dataImages?.articleData.map((e) => {
893
- delete e.src;
894
- e.imageID = e.image_id;
895
- // e.packingType = e.packing_type;
896
- // e.imageType = e.image_type;
897
- if (product?.orderId) e["orderId"] = product?.orderId;
898
- return e;
899
- });
900
- try {
901
- const res = await axios.put(
902
- `${process.env.REACT_APP_ARTICLE_DATA_ENDPOINT}?image=true&version=${version}`,
903
- dataImages,
904
- {
905
- headers: {
906
- Authorization: token,
907
- },
908
- }
909
- );
910
- if (res.data.statusCode === 200) {
911
- let productTemp = product;
912
- const { newStatus, newArticleStatus } = JSON.parse(res.data.body);
913
- if (newArticleStatus)
914
- productTemp.status = newArticleStatus[product?.article?.id_article];
915
- if (newStatus) productTemp.images_status = newStatus;
916
- setProduct(productTemp);
917
- sessionStorage.setItem(
918
- "productSelected",
919
- JSON.stringify(productTemp)
920
- );
921
- setImages({});
922
- setMessage("Imágenes guardadas con éxito");
923
- sessionStorage.removeItem("imagesList");
924
- }
925
- await loadData();
926
- } catch (error) {
927
- console.log(error);
535
+ const allowedRoles = [1, 4, 5, 6, 7, 8];
536
+ const validUser = allowedRoles.includes(user?.id_role);
537
+
538
+ // Determinar rol por defecto si no se pasa
539
+ if (!rol) {
540
+ if (user.id_role === 4 || user.id_role === 5) {
541
+ rol = "facilitator";
542
+ } else if (user.id_role === 7 || user.id_role === 8) {
543
+ rol = "especialist";
928
544
  }
929
545
  }
930
- }, [dataImages, imagesUploaded]);
931
546
 
932
- const evaluationFinished = (userId, tab, statusArray) => {
933
- const srv = servicesData.filter((serv) => serv.service === getConcept(tab));
934
- const srvActive = srv
935
- .find((serv) => serv.id_retailer === activeRetailer?.id)
936
- ?.status?.replace(/.*\//, "");
937
- const currStatus = product[`${getConcept(tab)}_status`]?.replace(
938
- /.*\//,
939
- ""
940
- );
941
- const unvalidated = ["IE", "CA"].includes(currStatus);
942
-
943
- const auditorUnvalidated = !["RA", "AA", "ACA", "AP"].includes(currStatus);
944
-
945
- switch (userId) {
946
- case 7:
947
- case 8:
948
- return (
949
- (statusArray.includes(srvActive) &&
950
- statusArray.includes(product?.status)) ||
951
- srv.filter((serv) =>
952
- statusArray.includes(serv.status?.replace(/.*\//, ""))
953
- ).length === srv.length
954
- );
955
- case 4:
956
- case 5:
957
- return (
958
- unvalidated &&
959
- ["CA", "IE"].includes(product.status) && // "RC", "AC", "AA", "AP", "ACA"
960
- srv.filter((serv) => statusArray.includes(serv.status)).length ===
961
- srv.length
962
- );
963
- case 6:
964
- return (
965
- statusArray.includes(product.status) && // RP, RCA, AC, RA true
966
- srv.every((serv) =>
967
- ["RA", "AA", "AP", "ACA"].includes(serv.status)
968
- ) &&
969
- auditorUnvalidated
970
- );
971
- default:
972
- break;
973
- }
974
- };
547
+ const isAdmin = user.id_role === 1;
548
+ const idUserAssigned = state.product?.[`id_${concept}_${rol}`];
549
+ const isAssigned = idUserAssigned === user.id_user;
975
550
 
976
- const getConcept = (tab) => {
977
- switch (tab) {
978
- case "Descripción":
979
- return "description";
980
- case "Ficha técnica":
981
- return "datasheet";
982
- case "Imágenes":
983
- return "images";
984
- }
551
+ return isAdmin || (isAssigned && validUser);
985
552
  };
986
553
 
987
- const approveRejectButtons = (action) => {
988
- let concept = getConcept(action || activeTab);
554
+ const canShowSingleEvaluationButtons = (action) => {
555
+ let concept = getConceptByTab(action || state.active_tab);
989
556
 
990
- const retailerStatus = servicesData
991
- .find(
557
+ const retailerStatus = state.services_data
558
+ ?.find(
992
559
  (srv) =>
993
- srv.id_retailer === activeRetailer?.id && srv.service === concept
560
+ srv.id_retailer === state.active_retailer?.id_retailer &&
561
+ srv.service === concept,
994
562
  )
995
563
  ?.status?.replace(/.*\//, "");
996
564
 
997
- //sessionStorage product
998
565
  const adminFacilitatorCanEvaluate =
999
566
  retailerStatus === "IE" && [1, 4, 5].includes(user.id_role);
1000
567
  const adminAuditorCanEvaluate =
1001
568
  ["AC", "RP", "RCA", "SAC"].includes(retailerStatus) &&
1002
569
  [1, 6].includes(user.id_role);
570
+
1003
571
  return adminFacilitatorCanEvaluate || adminAuditorCanEvaluate;
1004
572
  };
1005
573
 
1006
- const approveRejectAllButtons = () => {
1007
- let concepts = [];
1008
- switch (user.id_role) {
1009
- case 4:
1010
- concepts = ["description", "datasheet"];
1011
- break;
1012
- case 5:
1013
- concepts = ["images"];
1014
- break;
574
+ const canShowBulkEvaluationButtons = () => {
575
+ const concepts = getConceptsByRole(user.id_role);
1015
576
 
1016
- default:
1017
- concepts = ["description", "datasheet", "images"];
1018
- break;
1019
- }
1020
-
1021
- const services = servicesData.filter(({ service }) =>
1022
- concepts.includes(service)
577
+ const services = state.services_data.filter(({ service }) =>
578
+ concepts.includes(service),
1023
579
  );
1024
580
 
1025
581
  const adminFacilitatorCanEvaluate =
1026
582
  services.every((srv) => srv?.status?.replace(/.*\//, "") === "IE") &&
1027
583
  [1, 4, 5].includes(user.id_role);
1028
584
 
1029
- //sessionStorage product
1030
585
  const adminAuditorCanEvaluate =
1031
- servicesData.every((srv) =>
1032
- ["AC", "RP", "RCA"].includes(srv?.status?.replace(/.*\//, ""))
586
+ state.services_data.every((srv) =>
587
+ ["AC", "RP", "RCA"].includes(srv?.status?.replace(/.*\//, "")),
1033
588
  ) && [1, 6].includes(user.id_role);
1034
589
  return adminFacilitatorCanEvaluate || adminAuditorCanEvaluate;
1035
590
  };
1036
591
 
1037
- const getSectionIcon = () => {
1038
- switch (activeTab) {
1039
- case "Ficha técnica":
1040
- setIcon(attributesSent);
1041
- break;
592
+ const getStatusByCurrentServiceAndRetailer = () => {
593
+ const { statusByRetailer } = state.product;
594
+ if (!statusByRetailer) return "-";
595
+
596
+ const currentService = getConceptByTab(state.active_tab);
597
+ const currentRetailer = state.active_retailer.id_retailer;
598
+ const currentStatus =
599
+ statusByRetailer.filter(
600
+ (item) =>
601
+ item.retailer_id === currentRetailer &&
602
+ item.service === currentService,
603
+ )[0]?.status || "-";
604
+
605
+ return currentStatus;
606
+ };
607
+
608
+ const handleOnClickSaveImages = () => {
609
+ if (state.product?.services?.images === 1) {
610
+ saveImageAttrs(token);
611
+ }
612
+ };
613
+
614
+ const handleOnClickSave = async () => {
615
+ if (state.saving) return;
616
+
617
+ switch (state.active_tab) {
1042
618
  case "Descripción":
1043
- setIcon(descriptionSent);
619
+ await saveDescriptions(token);
620
+ break;
621
+ case "Ficha técnica":
622
+ await saveDatasheets(token);
1044
623
  break;
1045
624
  case "Imágenes":
1046
- setIcon(imagesSent);
625
+ await updateImages(token);
1047
626
  break;
1048
627
  default:
1049
628
  break;
1050
629
  }
630
+
631
+ await loadData(false);
1051
632
  };
1052
633
 
1053
- const sendToFacilitator = async (result) => {
1054
- if (!loading) setLoading(true);
634
+ const handleOnSetUpdatedDescriptions = (items) => {
635
+ dispatch({
636
+ type: "SET_UPDATED_DESCRIPTIONS_INPUTS",
637
+ payload: items,
638
+ });
639
+ };
640
+
641
+ const handleOnSetUpdatedDatasheets = (items) => {
642
+ dispatch({
643
+ type: "SET_UPDATED_DATASHEETS_INPUTS",
644
+ payload: items,
645
+ });
646
+ };
647
+
648
+ const handleOnAskToDeleteImages = () => {
649
+ if (state.selected_images.length === 0) {
650
+ dispatch({
651
+ type: "SET_MODAL",
652
+ payload: {
653
+ show: true,
654
+ title: "Eliminar imágenes",
655
+ message: "No has seleccionado ninguna imagen para eliminar.",
656
+ image: warningIcon,
657
+ },
658
+ });
659
+ return;
660
+ }
661
+
662
+ dispatch({
663
+ type: "SET_MODAL",
664
+ payload: {
665
+ show: true,
666
+ title: "Eliminar imágenes",
667
+ message:
668
+ "¿Estás seguro de que deseas eliminar las imágenes seleccionadas?",
669
+ image: warningIcon,
670
+ buttons: [
671
+ {
672
+ text: "Cancelar",
673
+ buttonType: "general-white-button",
674
+ action: () =>
675
+ dispatch({ type: "SET_MODAL", payload: { show: false } }),
676
+ },
677
+ {
678
+ text: "Eliminar",
679
+ buttonType: "general-button-default",
680
+ action: () => {
681
+ dispatch({ type: "SET_MODAL", payload: { show: false } });
682
+ deleteImages(token);
683
+ },
684
+ },
685
+ ],
686
+ },
687
+ });
688
+ };
689
+
690
+ const handleOnApproveSingleService = async () => {
691
+ await sendSingleEvaluation("A");
692
+ };
693
+
694
+ const handleOnRejectSingleService = () => {
695
+ setShowRejectModal(true);
696
+ };
697
+
698
+ const sendSingleEvaluation = async (result) => {
699
+ if (state.saving) return;
700
+
701
+ dispatch({ type: "SET_SAVING", payload: true });
702
+
703
+ const concept = getConceptByTab(state.active_tab);
704
+
705
+ const articleId = state.product.id_article;
706
+ const orderId = state.product.id_order ?? state.product.orderId;
707
+ const sectionStatusKey = `${concept}_status`;
708
+ const evalStatus = state.product[sectionStatusKey];
709
+ const retailerId = state.active_retailer?.id_retailer;
710
+
711
+ let data = { articleId, orderId, concept, evalStatus, retailerId };
712
+ let res;
713
+ let message;
714
+ let icon;
715
+
716
+ const activeTab = state.active_tab;
717
+
1055
718
  try {
1056
- let concept = getConcept(activeTab);
1057
-
1058
- const productTemp = { ...product };
1059
- const evalStatus = retailerStatus;
1060
- const articleId = product.article.id_article;
1061
- const orderId = product.orderId;
1062
-
1063
- let data = {
1064
- articleId,
1065
- orderId,
1066
- concept,
1067
- evalStatus,
1068
- retailerId: activeRetailer.id,
1069
- };
1070
- let res;
1071
- let message;
719
+ //Esto solo se ejecutará cuando se aprueba o rechaza desde el modal superior derecho
1072
720
  if (result) {
1073
721
  data.result = result;
722
+ data.retailerId = retailerId;
723
+
1074
724
  res = await axios.put(
1075
725
  `${process.env.REACT_APP_EVALUATION_ENDPOINT}`,
1076
726
  data,
@@ -1078,254 +728,297 @@ export const RetailerProductEdition = ({
1078
728
  headers: {
1079
729
  Authorization: token,
1080
730
  },
1081
- }
731
+ },
732
+ );
733
+
734
+ const newStatuses = JSON.parse(res.data.body);
735
+ const serviceStatus = newStatuses.newServiceStatus[articleId][`${concept}Status`];
736
+ const articleStatus = newStatuses.newArticleStatus[articleId];
737
+
738
+ console.log({newStatuses});
739
+
740
+ // Actualizar el producto con los nuevos estados
741
+ const updatedStatusByRetailer = state.product.statusByRetailer.map(
742
+ (item) =>
743
+ item.retailer_id === retailerId && item.service === concept
744
+ ? { ...item, status: serviceStatus }
745
+ : item,
1082
746
  );
1083
- getServices();
1084
- } else {
747
+
748
+ const updatedProduct = {
749
+ ...state.product,
750
+ statusByRetailer: updatedStatusByRetailer,
751
+ // version_status: articleStatus,
752
+ // status: articleStatus,
753
+ // article_status: articleStatus
754
+ };
755
+
756
+ dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
757
+
758
+ // Mostrar modal de éxito
759
+ dispatch({
760
+ type: "SET_MODAL",
761
+ payload: {
762
+ show: true,
763
+ title: "",
764
+ message:
765
+ result === "A" ? "Aprobado con éxito" : "Rechazado con éxito",
766
+ image: successIcon,
767
+ },
768
+ });
769
+ } else { //Caso del botón "Enviar evaluación"
770
+
771
+ //Se construye el mensaje y se actualiza el estatus
772
+
1085
773
  const specialistDone = ["RC", "RA", "CA"].includes(evalStatus);
1086
774
 
1087
775
  if (specialistDone) {
1088
776
  message = `${activeTab} enviada a facilitador`;
1089
- getSectionIcon();
777
+ icon =
778
+ activeTab === "Descripción"
779
+ ? descriptionSent
780
+ : activeTab === "Ficha técnica"
781
+ ? attributesSent
782
+ : imagesSent;
1090
783
  } else if (["IE", "AC", "RP", "RCA"].includes(evalStatus)) {
1091
784
  message = "Evaluación enviada";
1092
- getSectionIcon();
785
+ icon = successIcon;
1093
786
  }
787
+
1094
788
  res = await axios.put(`${process.env.REACT_APP_SEND_EVAL}`, data, {
1095
789
  headers: {
1096
790
  Authorization: token,
1097
791
  },
1098
792
  });
1099
793
  }
794
+
1100
795
  if (res.data.statusCode === 200) {
1101
- const { newStatus, newOrderStatus, newArticleStatus } = JSON.parse(
1102
- res.data.body
1103
- );
1104
- const messageToChat = createMessage(
1105
- product.retailers,
1106
- activeRetailer.id,
1107
- evalStatus,
1108
- newStatus,
1109
- activeTab
1110
- );
1111
796
 
1112
- const data = {
1113
- paramsBody: {
1114
- id: product.article.id_article,
1115
- version: version,
1116
- items: [{ type: "status", value: messageToChat }],
1117
- retailerId: activeRetailer.id,
1118
- status: product.status,
1119
- },
1120
- paramsHeader: { Authorization: token },
797
+ const { newStatus, newOrderStatus, newArticleStatus } = JSON.parse( res.data.body );
798
+
799
+ const articleStatus = newArticleStatus[articleId];
800
+
801
+ console.log({newArticleStatus});
802
+
803
+ // const retailers = state.product.categoryRetailer.map((r) => ({
804
+ // id: r.id_retailer,
805
+ // name: r.retailerName,
806
+ // }));
807
+ // const messageToChat = createMessage(
808
+ // retailers,
809
+ // retailerId,
810
+ // evalStatus,
811
+ // newStatus,
812
+ // activeTab,
813
+ // );
814
+
815
+ // const messageData = {
816
+ // paramsBody: {
817
+ // id: articleId,
818
+ // version: state.product.version,
819
+ // items: [{ type: "status", value: messageToChat }],
820
+ // retailerId: retailerId,
821
+ // status: state.product.status,
822
+ // },
823
+ // paramsHeader: { Authorization: token },
824
+ // };
825
+ // await sendMessage(messageData);
826
+
827
+ const updatedProduct = {
828
+ ...state.product,
829
+ [`${concept}_status`]: newStatus,
830
+ // actualizar el status es statusByRetailer
831
+ statusByRetailer: state.product.statusByRetailer.map((item) => {
832
+ if (item.retailer_id === retailerId && item.service === concept) {
833
+ return { ...item, status: newStatus };
834
+ }
835
+ return item;
836
+ }),
837
+ // version_status: articleStatus,
838
+ // status: articleStatus,
839
+ // article_status: articleStatus
1121
840
  };
1122
- await sendMessage(data);
1123
- if (newOrderStatus) productTemp.status = newArticleStatus[articleId];
1124
- productTemp[`${concept}_status`] = newStatus;
1125
- await loadData();
1126
- if (message) setMessage(message);
1127
- sessionStorage.setItem("productSelected", JSON.stringify(productTemp));
1128
- setProduct(productTemp);
1129
- }
1130
- } catch (error) {
1131
- setLoading(false);
1132
- console.log(error);
1133
- }
1134
- };
1135
841
 
1136
- const userAssigned = (tab, rol) => {
1137
- let concept = getConcept(activeTab);
842
+ //Actualizamos el services_data en el servicio específico del retailer actual
1138
843
 
1139
- const allowedRoles = [1, 4, 5, 6, 7, 8];
1140
- const validUser = allowedRoles.includes(user?.id_role);
844
+ const updatedServicesData = state.services_data?.map(service => ({
845
+ ...service,
846
+ status: service?.id_retailer === retailerId && service?.service === concept ? newStatus : service?.status
847
+ }));
848
+
849
+ const sessionProduct = sessionStorage.getItem("productSelected");
850
+
851
+ if (sessionProduct) {
852
+ const productInSession = JSON.parse(sessionProduct);
853
+ const updatedProductInSession = {
854
+ ...productInSession,
855
+ [`${concept}_status`]: newStatus,
856
+ statusByRetailer: state.product.statusByRetailer.map((item) => {
857
+ if (item.retailer_id === retailerId && item.service === concept) {
858
+ return { ...item, status: newStatus };
859
+ }
860
+ return item;
861
+ }),
862
+ };
863
+ sessionStorage.setItem(
864
+ "productSelected",
865
+ JSON.stringify(updatedProductInSession),
866
+ );
867
+ }
868
+
869
+ if (message) {
870
+ dispatch({
871
+ type: "SET_MODAL",
872
+ payload: {
873
+ show: true,
874
+ title: "",
875
+ message: message,
876
+ image: successIcon,
877
+ },
878
+ });
879
+ }
1141
880
 
1142
- if (!rol) {
1143
- switch (user.id_role) {
1144
- case 4:
1145
- case 5:
1146
- rol = "facilitator";
1147
- break;
1148
- case 7:
1149
- case 8:
1150
- rol = "especialist";
1151
- break;
881
+ dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
882
+ dispatch({ type: "SET_SERVICES_DATA", payload: updatedServicesData });
1152
883
  }
884
+ } catch (error) {
885
+ console.error("Error sending evaluation:", error);
886
+ dispatch({
887
+ type: "SET_MODAL",
888
+ payload: {
889
+ show: true,
890
+ title: "Error",
891
+ message: "Hubo un error al procesar la evaluación",
892
+ image: errorIcon,
893
+ },
894
+ });
895
+ } finally {
896
+ dispatch({ type: "SET_SAVING", payload: false });
1153
897
  }
1154
-
1155
- return (
1156
- user.id_role === 1 ||
1157
- (product.article[`id_${concept}_${rol}`] === user.id_user && validUser)
1158
- );
1159
898
  };
1160
899
 
1161
- const auditorAssigned = () => {
1162
- return product?.article[`id_auditor`] === user.id_user;
900
+ const handleOnApproveAllServices = async () => {
901
+ await sendBulkEvaluation("A");
1163
902
  };
1164
903
 
1165
- const createComment = async (e, body, tab) => {
1166
- let concept = "";
1167
- switch (activeTab) {
1168
- case "Ficha técnica":
1169
- concept = "datasheet";
1170
- break;
1171
- case "Imágenes":
1172
- concept = "images";
1173
- break;
1174
-
1175
- default:
1176
- concept = "description";
1177
- break;
1178
- }
1179
- const data = {
1180
- articleId: product?.article?.id_article,
1181
- orderId: product?.orderId,
1182
- message: body?.replace(/<.*?\/?>/gm, ""),
1183
- concept: concept,
1184
- version: version,
1185
- };
1186
- await axios.post(`${process.env.REACT_APP_COMMENTS_ENDPOINT}`, data, {
1187
- headers: {
1188
- Authorization: token,
1189
- },
1190
- });
1191
- await getComments(tab);
1192
- setMessage("");
1193
- setComponentsArray([]);
904
+ const handleOnRejectAllServices = () => {
905
+ setRejectAll(true);
906
+ setShowRejectModal(true);
1194
907
  };
1195
908
 
1196
- const getRequired = (services) => {
909
+ const sendBulkEvaluation = async (result) => {
910
+ dispatch({ type: "SET_SAVING", payload: true });
1197
911
  try {
1198
- const objetcTemp = {};
1199
- const datasheetServicesArray = Object.values(services[0]);
1200
- const allDatasheetInputs = datasheetServicesArray.pop();
1201
- const descriptionsServicesArray = services[1];
1202
-
1203
- let dsInputsRequired = [];
1204
-
1205
- let desInputsRequired = 0;
1206
- datasheetServicesArray?.forEach((datasheet) => {
1207
- const [requested] = servicesData?.filter(
1208
- (srv) =>
1209
- srv.id_retailer === datasheet.retailer.id &&
1210
- srv.service === getConcept(activeTab)
1211
- );
912
+ const evaluationArray = [];
1212
913
 
1213
- requested &&
1214
- datasheet?.data &&
1215
- Object.values(datasheet?.data).forEach((dataGroup) => {
1216
- dsInputsRequired.push(
1217
- ...dataGroup.inputs.filter((input) => {
1218
- return (
1219
- allDatasheetInputs[input].required &&
1220
- allDatasheetInputs[input].id_retailer === activeRetailer.id &&
1221
- (allDatasheetInputs[input].value === undefined ||
1222
- !allDatasheetInputs[input].value)
1223
- );
1224
- })
1225
- );
1226
- });
1227
- });
914
+ const conceptArray = getConceptsByRole(user.id_role);
1228
915
 
1229
- objetcTemp["Ficha técnica"] = dsInputsRequired.length;
916
+ state.services_data?.forEach((ret, idx) => {
917
+ const { service: retailer_service, id_retailer, status: status_retailer_service } = ret;
1230
918
 
1231
- const regex = /(<\/?p>)|(<\/?strong>)|(<br>)/gm;
1232
- descriptionsServicesArray.forEach((description) => {
1233
- if (description.id != activeRetailer.id) return;
919
+ const lastStatusLevel = status_retailer_service.replace(/.*\//, "");
1234
920
 
1235
- const [requested] = servicesData.filter(
1236
- (srv) =>
1237
- srv.id_retailer === description.id &&
1238
- srv.service === getConcept(activeTab)
1239
- );
921
+ if (conceptArray.includes(retailer_service) && statusArray?.includes(lastStatusLevel)) {
922
+ const data = {
923
+ articleId: state.product.id_article,
924
+ orderId: state.product.id_order,
925
+ concept: retailer_service,
926
+ result,
927
+ evalStatus: status_retailer_service,
928
+ retailerId: id_retailer,
929
+ };
1240
930
 
1241
- if (requested) {
1242
- description.inputs.forEach((input) => {
1243
- if (
1244
- input.required &&
1245
- (!input.value || input.value?.replace(regex, "") === "")
1246
- ) {
1247
- desInputsRequired++;
1248
- }
1249
- });
931
+ evaluationArray.push(
932
+ axios.put(`${process.env.REACT_APP_EVALUATION_ENDPOINT}`, data, {
933
+ headers: {
934
+ Authorization: token,
935
+ },
936
+ }),
937
+ );
1250
938
  }
1251
939
  });
1252
940
 
1253
- objetcTemp["Descripción"] = desInputsRequired;
941
+ await Promise.all(evaluationArray);
1254
942
 
1255
- const retailersRequested = [];
943
+ const status = `${result}A`;
1256
944
 
1257
- services[2]?.retailerMandatories?.forEach((retMan) =>
1258
- retMan.forEach((rm) => retailersRequested.push(rm))
945
+ // Actualizar el statusByRetailer para todos los retailers y conceptos
946
+ const updatedStatusByRetailer = state.product.statusByRetailer.map(
947
+ (item) =>
948
+ conceptArray.includes(item.service) ? { ...item, status } : item,
1259
949
  );
1260
950
 
1261
- const requiredImages = services[2]?.inputs?.filter(
1262
- (e) =>
1263
- e.required === 1 &&
1264
- retailersRequested.filter(
1265
- (ret) =>
1266
- ret.id_image === e.id &&
1267
- servicesData.filter((srv) => srv.id_retailer === ret.id_retailer)
1268
- .length > 0
1269
- ).length > 0
1270
- );
951
+ // Actualizar el producto con los nuevos estados
952
+ const updatedProduct = {
953
+ ...state.product,
954
+ article_status: status,
955
+ status: status,
956
+ datasheet_status:
957
+ state.product.datasheet_status === "NA" ? "NA" : status,
958
+ description_status:
959
+ state.product.description_status === "NA" ? "NA" : status,
960
+ images_status: state.product.images_status === "NA" ? "NA" : status,
961
+ statusByRetailer: updatedStatusByRetailer,
962
+ };
1271
963
 
1272
- let requiredCounter = 0;
1273
- requiredImages?.forEach((req) => {
1274
- return (
1275
- services[2].values.filter((img) => img.image_id === req.id).length ===
1276
- 0 && requiredCounter++
1277
- );
964
+ dispatch({
965
+ type: "SET_PRODUCT",
966
+ payload: updatedProduct,
1278
967
  });
1279
- objetcTemp["Imágenes"] = requiredCounter;
1280
- setRequiredNull(objetcTemp);
1281
- } catch (error) {
1282
- console.log(error);
1283
- }
1284
- };
1285
-
1286
- useEffect(() => {
1287
- setComment(comments[activeTab]);
1288
- }, [activeTab]);
1289
968
 
1290
- const commentRevised = async () => {
1291
- const data = {
1292
- commentId: comment.id,
1293
- };
1294
- await axios.put(`${process.env.REACT_APP_COMMENTS_ENDPOINT}`, data, {
1295
- headers: {
1296
- Authorization: sessionStorage.getItem("jwt"),
1297
- },
1298
- });
1299
- setCrossComment(false);
1300
- await getComments();
1301
- };
969
+ const updatedServicesData = state.services_data.map(service => conceptArray.includes(service.service) ? { ...service, status: status } : service);
1302
970
 
1303
- const setAssignation = async (assignationType, assignationId) => {
1304
- let concept = "";
1305
- switch (activeTab) {
1306
- case "Ficha técnica":
1307
- concept = "datasheet";
1308
- break;
1309
- case "Imágenes":
1310
- concept = "images";
1311
- break;
971
+ dispatch({
972
+ type: "SET_SERVICES_DATA",
973
+ payload: updatedServicesData,
974
+ });
1312
975
 
1313
- default:
1314
- concept = "description";
1315
- break;
976
+ // sessionStorage.setItem(
977
+ // "productSelected",
978
+ // JSON.stringify(updatedProduct),
979
+ // );
980
+ // sessionStorage.setItem(
981
+ // "productEdit",
982
+ // JSON.stringify({
983
+ // ArticleId: updatedProduct.id_article,
984
+ // idCategory: updatedProduct.id_category,
985
+ // product: updatedProduct,
986
+ // }),
987
+ // );
988
+
989
+ // Mostrar modal de éxito
990
+ dispatch({
991
+ type: "SET_MODAL",
992
+ payload: {
993
+ show: true,
994
+ title: "",
995
+ message:
996
+ result === "A"
997
+ ? "Todos los servicios aprobados con éxito"
998
+ : "Todos los servicios rechazados con éxito",
999
+ image: successIcon,
1000
+ },
1001
+ });
1002
+ } catch (error) {
1003
+ console.error("Error in bulk evaluation:", error);
1004
+ } finally {
1005
+ dispatch({ type: "SET_SAVING", payload: false });
1316
1006
  }
1317
- const productTemp = product;
1318
- productTemp.article[`id_${concept}_${assignationType}`] = assignationId;
1007
+ };
1008
+
1009
+ const handleOnChangeAssignations = async (assignationType, assignationId) => {
1010
+ const concept = getConceptByTab(state.active_tab);
1319
1011
  const data = {
1320
1012
  articleList: [
1321
1013
  {
1322
- orderId: product.orderId,
1323
- articleId: product?.article?.id_article,
1014
+ orderId: state.product.id_order,
1015
+ articleId: state.product.id_article,
1324
1016
  },
1325
1017
  ],
1326
1018
  concept: concept,
1327
1019
  userId: assignationId,
1328
1020
  };
1021
+
1329
1022
  await axios({
1330
1023
  method: "post",
1331
1024
  url: process.env.REACT_APP_ASSIGNATIONS_ENDPOINT,
@@ -1334,646 +1027,799 @@ export const RetailerProductEdition = ({
1334
1027
  Authorization: token,
1335
1028
  },
1336
1029
  });
1337
- loadAssignations(productTemp);
1338
- sessionStorage.setItem("productSelected", JSON.stringify(productTemp));
1339
- };
1340
1030
 
1341
- const downloadImages = () => {
1342
- selectedImages.length > 0
1343
- ? selectedImages.forEach((e) => {
1344
- if (e.id) {
1345
- saveAs(
1346
- `https://${process.env.REACT_APP_IMAGES_BUCKET}.s3.amazonaws.com/${e.srcDB}`,
1347
- `${product.article.upc}_${e.name}.${e.ext}`
1348
- );
1349
- }
1350
- })
1351
- : images?.values?.forEach((e) => {
1352
- if (e.id) {
1353
- saveAs(
1354
- `https://${process.env.REACT_APP_IMAGES_BUCKET}.s3.amazonaws.com/${e.srcDB}`,
1355
- `${product.article.upc}_${e.name}.${e.ext}`
1356
- );
1357
- }
1358
- });
1031
+ const updatedProduct = {
1032
+ ...state.product,
1033
+ [`id_${concept}_${assignationType}`]: assignationId,
1034
+ };
1035
+
1036
+ dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
1359
1037
  };
1360
1038
 
1361
- const deleteImages = async () => {
1362
- setLoading(true);
1363
- const { values } = images;
1364
- const imgsInBack = [];
1039
+ const handleOnSetAssignation = () => {
1040
+ console.log("handleOnSetAssignation - pendiente de implementar");
1041
+ };
1365
1042
 
1366
- selectedImages.forEach((selectedImg) => {
1367
- if (selectedImg.id) imgsInBack.push(selectedImg);
1368
- });
1043
+ const handleOnSetImages = () => {
1044
+ console.log("handleOnSetImages - pendiente de implementar");
1045
+ };
1369
1046
 
1370
- const imgsLeft = values.filter(
1371
- (value) => selectedImages.indexOf(value) === -1
1372
- );
1047
+ const handleOnSetCheckAll = (isChecked) => {
1048
+ dispatch({ type: "TOGGLE_CHECK_ALL_IMAGES", payload: isChecked });
1049
+ };
1373
1050
 
1374
- if (imgsInBack.length > 0) {
1375
- const data = {
1376
- articleId: product.article.id_article,
1377
- deleteImages: imgsInBack,
1378
- orderId: product.orderId,
1379
- };
1380
- try {
1381
- await axios.put(
1382
- `${process.env.REACT_APP_ARTICLE_DATA_ENDPOINT}?image=true&version=${version}`,
1383
- data,
1384
- {
1385
- headers: { Authorization: token },
1386
- }
1387
- );
1388
- } catch (err) {
1389
- console.log(err);
1390
- }
1051
+ const handleOnSetSelectedImages = () => {
1052
+ /*
1053
+ Esta función no se utiliza actualmente.
1054
+
1055
+ En la versión anterior se usaban dos funciones distintas para el checkbox
1056
+ "Seleccionar todo":
1057
+ - Una para cambiar el valor booleano del checkbox.
1058
+ - Otra para actualizar el array de imágenes seleccionadas.
1059
+
1060
+ En la versión actual se unificó la lógica en una sola función,
1061
+ `handleOnSetCheckAll`, que recibe el valor `true` o `false` del checkbox
1062
+ "Seleccionar todo" y, a partir de este, agrega o elimina todas las imágenes
1063
+ del array de imágenes seleccionadas.
1064
+ */
1065
+ console.log("setSelectedImages");
1066
+ };
1067
+ const handleOnSubmitComment = async (e) => {
1068
+ e.preventDefault();
1069
+ const commentText = document.querySelector(
1070
+ "#commentary-box .ql-container .ql-editor > p",
1071
+ ).innerHTML;
1072
+ try {
1073
+ await createComment(commentText, token);
1074
+
1075
+ dispatch({
1076
+ type: "SET_MODAL",
1077
+ payload: {
1078
+ show: true,
1079
+ title: "",
1080
+ message: "Comentario guardado con éxito",
1081
+ image: successIcon,
1082
+ },
1083
+ });
1084
+ } catch (error) {
1085
+ console.error("Error saving comment:", error);
1086
+ dispatch({
1087
+ type: "SET_MODAL",
1088
+ payload: {
1089
+ show: true,
1090
+ title: "Error",
1091
+ message: "Hubo un error al guardar el comentario",
1092
+ image: errorIcon,
1093
+ },
1094
+ });
1391
1095
  }
1096
+ };
1392
1097
 
1393
- setImages({
1394
- action: "deleteImage",
1395
- selectedImages,
1396
- });
1397
-
1398
- getRequired([
1399
- services[0],
1400
- services[1],
1401
- { ...services[2], values: imgsLeft },
1402
- ]);
1098
+ const handleOnSendEvaluationToFacilitator = () => {
1099
+ sendSingleEvaluation();
1100
+ };
1403
1101
 
1404
- setLoading(false);
1102
+ const isEvaluationFinished = (id_rol, tab, statusArray) => {
1103
+ const servicesData = state.services_data;
1104
+ const activeRetailer = state.active_retailer;
1105
+ const product = state.product;
1106
+ const concept = getConceptByTab(tab);
1405
1107
 
1406
- setMessage("");
1407
- setComponentsArray([]);
1408
- };
1108
+ const servicesByTab = servicesData?.filter(
1109
+ (serv) => serv.service === concept,
1110
+ );
1409
1111
 
1410
- const askToDeleteImages = () => {
1411
- if (selectedImages.length > 0) {
1412
- setMessage("¿Está seguro de eliminar las imágenes seleccionadas?");
1413
- setComponentsArray([
1414
- <ScreenHeader
1415
- key={"1"}
1416
- text={"¿Está seguro de eliminar las imágenes seleccionadas?"}
1417
- headerType="retailer-name-header"
1418
- color={"white"}
1419
- />,
1420
- <Button
1421
- key={"2"}
1422
- buttonType="general-white-button"
1423
- label={"Cancelar"}
1424
- onClick={() => setMessage("")}
1425
- />,
1426
- <Button
1427
- key={"3"}
1428
- buttonType="general-button-default"
1429
- label={"Aceptar"}
1430
- onClick={() => {
1431
- setMessage("");
1432
- deleteImages();
1433
- }}
1434
- />,
1435
- ]);
1436
- }
1437
- };
1112
+ const statusBySelectedRetailerService = servicesByTab
1113
+ ?.find((serv) => serv.id_retailer === activeRetailer?.id_retailer)
1114
+ ?.status?.replace(/.*\//, "");
1438
1115
 
1439
- const getRetailerStatus = (servicesData, tab) => {
1440
- const arr = servicesData?.slice();
1441
- let concept = getConcept(tab);
1116
+ const currentProducStatus = product?.[`${concept}_status`]?.replace(
1117
+ /.*\//,
1118
+ "",
1119
+ );
1442
1120
 
1443
- let retailerService = {};
1444
- [retailerService] = arr?.filter(
1445
- (service) =>
1446
- service.id_retailer === activeRetailer?.id &&
1447
- service.service === concept
1121
+ const unvalidated = ["IE", "CA"].includes(currentProducStatus);
1122
+ const auditorUnvalidated = !["RA", "AA", "ACA", "AP"].includes(
1123
+ currentProducStatus,
1448
1124
  );
1449
- return retailerService ? retailerService.status : "NS";
1450
- };
1451
1125
 
1452
- useEffect(() => {
1453
- let status = getRetailerStatus(servicesData, activeTab);
1454
- setRetailerStatus(status);
1455
- }, [activeTab, servicesData, activeRetailer]);
1126
+ let result;
1127
+ switch (id_rol) {
1128
+ case 7:
1129
+ case 8:
1456
1130
 
1457
- useEffect(() => {
1458
- services.length > 0 && getRequired(services);
1459
- }, [services, servicesData, activeTab]);
1131
+ const canSendEvaluation = statusArray.includes(statusBySelectedRetailerService);
1460
1132
 
1461
- useEffect(() => {
1462
- setSaving(loading);
1463
- }, [loading]);
1133
+ // const conditionTwo = statusArray.includes(product?.status);
1464
1134
 
1465
- const validateAll = async (result) => {
1466
- try {
1467
- setLoading(true);
1468
- const evaluationArray = [];
1469
- let conceptArray = [];
1470
- switch (user.id_role) {
1471
- case 4:
1472
- conceptArray = ["description", "datasheet"];
1473
- break;
1474
- case 5:
1475
- conceptArray = ["images"];
1476
- break;
1477
-
1478
- default:
1479
- conceptArray = ["description", "datasheet", "images"];
1480
- break;
1481
- }
1135
+ // const conditionThree =
1136
+ // servicesByTab.filter((service) =>
1137
+ // statusArray.includes(service.status?.replace(/.*\//, "")),
1138
+ // ).length === servicesByTab.length;
1482
1139
 
1483
- const messages = [];
1484
-
1485
- servicesData?.forEach((ret) => {
1486
- if (conceptArray.includes(ret.service)) {
1487
- let data = {
1488
- articleId: product.article.id_article,
1489
- orderId: product.id_order ?? product.orderId,
1490
- concept: ret.service,
1491
- result: result,
1492
- evalStatus: ret.status,
1493
- retailerId: ret.id_retailer,
1494
- };
1495
- evaluationArray.push(
1496
- axios.put(`${process.env.REACT_APP_EVALUATION_ENDPOINT}`, data, {
1497
- headers: {
1498
- Authorization: token,
1499
- },
1500
- })
1501
- );
1502
- }
1503
- });
1504
- await Promise.all(evaluationArray);
1140
+ // return (canSendEvaluation && conditionTwo) || conditionThree;
1505
1141
 
1506
- const productTemp = product;
1507
- productTemp.status = `${result}A`;
1508
- productTemp.datasheet_status =
1509
- productTemp.datasheet_status === "NS" ? "NS" : `${result}A`;
1510
- productTemp.description_status =
1511
- productTemp.description_status === "NS" ? "NS" : `${result}A`;
1512
- productTemp.images_status =
1513
- productTemp.images_status === "NS" ? "NS" : `${result}A`;
1514
- sessionStorage.setItem("productSelected", JSON.stringify(productTemp));
1515
- setProduct(productTemp);
1516
- const messagesResponse = await Promise.all(messages);
1517
-
1518
- await loadData();
1519
- } catch (error) {
1520
- console.log(error);
1521
- } finally {
1522
- setLoading(false);
1142
+ return canSendEvaluation;
1143
+
1144
+ case 4:
1145
+ case 5:
1146
+ const filteredSrv45 = servicesByTab?.filter((serv) =>
1147
+ statusArray.includes(serv?.status),
1148
+ );
1149
+ const condition45 =
1150
+ unvalidated &&
1151
+ ["CA", "IE"].includes(product?.status) &&
1152
+ filteredSrv45?.length === servicesByTab?.length;
1153
+ return condition45;
1154
+ case 6:
1155
+ const statusInArray = statusArray.includes(product?.status);
1156
+ const allSrvValid = srv?.every((serv) =>
1157
+ ["RA", "AA", "AP", "ACA"].includes(serv?.status),
1158
+ );
1159
+ const condition6 = statusInArray && allSrvValid && auditorUnvalidated;
1160
+ return condition6;
1161
+ default:
1162
+ console.log("Default case - returning false");
1163
+ return false;
1523
1164
  }
1524
1165
  };
1525
1166
 
1526
- const getObservation = async () => {
1527
- const response = await axios.get(
1528
- `${process.env.REACT_APP_READ_OBSERVATION}?articleId=${product.article.id_article}&orderId=${product?.orderId}`,
1529
- {
1530
- headers: {
1531
- Authorization: sessionStorage.getItem("jwt"),
1532
- },
1533
- }
1167
+ const handleOnChangeProductVersion = (version) => {
1168
+ console.log('se cambia la version')
1169
+ dispatch({
1170
+ type: "SET_PRODUCT_VERSION",
1171
+ payload: version,
1172
+ });
1173
+
1174
+ loadData(true, version);
1175
+ };
1176
+
1177
+ const ProductEditionSkeleton = () => {
1178
+ return (
1179
+ <Container headerTop={0}>
1180
+ <Box
1181
+ px={1.5}
1182
+ py={1}
1183
+ pb={0}
1184
+ display="flex"
1185
+ alignItems="center"
1186
+ justifyContent="space-between"
1187
+ >
1188
+ <Skeleton variant="text" width={300} height={40} />
1189
+ <Stack direction="row" spacing={2}>
1190
+ <Skeleton variant="circular" width={32} height={32} />
1191
+ </Stack>
1192
+ </Box>
1193
+
1194
+ <div
1195
+ className="data-container"
1196
+ style={{ display: "flex", gap: "8px", padding: "0 12px 12px" }}
1197
+ >
1198
+ <div
1199
+ className="image-data-panel"
1200
+ style={{
1201
+ flex: "0 0 320px",
1202
+ display: "flex",
1203
+ flexDirection: "column",
1204
+ gap: "20px",
1205
+ }}
1206
+ >
1207
+ <Skeleton variant="rounded" width="100%" height={400} />
1208
+
1209
+ <Stack direction="row" spacing={2} justifyContent="center">
1210
+ {[1, 2, 3, 4].map((item) => (
1211
+ <Skeleton key={item} variant="rounded" width={60} height={80} />
1212
+ ))}
1213
+ </Stack>
1214
+
1215
+ <Box mt={2}>
1216
+ {[1, 2, 3, 4].map((item) => (
1217
+ <Box
1218
+ key={item}
1219
+ display="flex"
1220
+ justifyContent="space-between"
1221
+ py={1}
1222
+ borderBottom="1px solid #f0f0f0"
1223
+ >
1224
+ <Skeleton variant="text" width="40%" />
1225
+ <Skeleton variant="text" width="20%" />
1226
+ </Box>
1227
+ ))}
1228
+ </Box>
1229
+ </div>
1230
+
1231
+ <div
1232
+ className="product-information"
1233
+ style={{
1234
+ flex: 1,
1235
+ display: "flex",
1236
+ flexDirection: "column",
1237
+ gap: "14px",
1238
+ }}
1239
+ >
1240
+ <Box>
1241
+ <Skeleton variant="text" width="80%" height={50} />
1242
+ <Stack direction="row" spacing={2} alignItems="center">
1243
+ <Skeleton variant="rounded" width={180} height={24} />
1244
+ <Skeleton variant="rounded" width={300} height={24} />
1245
+ </Stack>
1246
+ </Box>
1247
+
1248
+ <Box display="flex" gap={4} pb={1}>
1249
+ <Skeleton variant="text" width={140} height={45} />
1250
+ <Skeleton variant="text" width={140} height={45} />
1251
+ <Skeleton variant="text" width={140} height={45} />
1252
+ </Box>
1253
+
1254
+ <Box display="flex" flexDirection="column" gap={4}>
1255
+ {[1, 2, 3].map((item) => (
1256
+ <Box key={item}>
1257
+ <Box
1258
+ display="flex"
1259
+ justifyContent="space-between"
1260
+ alignItems="center"
1261
+ mb={1}
1262
+ >
1263
+ <Skeleton variant="text" width="30%" height={24} />
1264
+ <Skeleton
1265
+ variant="rounded"
1266
+ width={175}
1267
+ height={25}
1268
+ sx={{ borderRadius: "7.5px" }}
1269
+ />
1270
+ </Box>
1271
+ <Skeleton variant="rounded" width="100%" height={70} />
1272
+ <Box display="flex" justifyContent="flex-end" mt={0.5}>
1273
+ <Skeleton variant="text" width={60} />
1274
+ </Box>
1275
+ </Box>
1276
+ ))}
1277
+ </Box>
1278
+
1279
+ <Box mt="auto" pt={4}>
1280
+ <Skeleton variant="rounded" width="100%" height={120} />
1281
+ <Box display="flex" justifyContent="flex-end" gap={2} mt={2}>
1282
+ <Skeleton
1283
+ variant="rounded"
1284
+ width={180}
1285
+ height={45}
1286
+ sx={{ borderRadius: "24px" }}
1287
+ />
1288
+ <Skeleton
1289
+ variant="rounded"
1290
+ width={180}
1291
+ height={45}
1292
+ sx={{ borderRadius: "24px" }}
1293
+ />
1294
+ </Box>
1295
+ </Box>
1296
+ </div>
1297
+ </div>
1298
+ </Container>
1534
1299
  );
1535
- const parseData = JSON.parse(response.data.body).data[0];
1536
- setObservation(parseData.observations);
1537
1300
  };
1301
+
1538
1302
  useEffect(() => {
1539
- getObservation();
1540
- }, []);
1303
+ console.log({stateProduct: state.product})
1304
+ }, [state.product]);
1305
+
1306
+ if (state.loading || !state.services || !state.product)
1307
+ return (
1308
+ <>
1309
+ <ProductEditionSkeleton />
1310
+ </>
1311
+ );
1312
+
1541
1313
  return (
1542
- <Container headerTop={headerTop}>
1543
- <HeaderTop
1544
- setHeaderTop={setHeaderTop}
1545
- auditableVersion={auditableVersion}
1546
- setCompare={setCompare}
1547
- isAuditor={[1, 6].includes(user.id_role)}
1548
- withChat={location?.state?.withChat}
1549
- chatType={location?.state?.chatType}
1550
- productSelected={product}
1551
- token={token}
1552
- activeRetailer={activeRetailer}
1553
- />
1554
- <div className="data-container">
1555
- <div className="image-data-panel">
1556
- <ImagePreviewer
1557
- activeImage={images?.values ? images?.values[activeImage] : {}}
1558
- imagesArray={images}
1559
- setActiveImage={setActiveImage}
1560
- setShowModal={setShowModal}
1561
- />
1562
- <ImageDataTable
1563
- lists={images}
1564
- activeImage={images?.values ? images?.values[activeImage] : {}}
1565
- retailerSelected={activeRetailer?.id}
1566
- setImages={setImages}
1567
- assignationsImages={assig["Imágenes"]}
1568
- imagesStatus={product?.images_status}
1569
- setAssignation={setAssignation}
1570
- isRetailer={isRetailer}
1571
- onClickSave={() =>
1572
- product?.services?.images === 1 && updateImages()
1314
+ <AiProductEditionProvider
1315
+ isCreatorsEdition={true}
1316
+ user={user}
1317
+ token={token}
1318
+ state={state}
1319
+ >
1320
+ <Container headerTop={headerTop}>
1321
+ {showRejectModal && (
1322
+ <Modal
1323
+ title={
1324
+ rejectAll
1325
+ ? "Agregar mensaje para rechazar todo"
1326
+ : "Agregar mensaje de rechazo"
1573
1327
  }
1574
- showSaveButton={auditorAssigned() || userAssigned()}
1575
- setShowVersionSelector={setShowVersionSelector}
1576
- version={version}
1577
- shotThd={shotThd}
1578
- />
1579
- </div>
1580
- <div className="product-information">
1581
- <FullProductNameHeader
1582
- headerData={product}
1583
- productObservation={observation}
1584
- percent={activePercentage}
1585
- activeRetailer={activeRetailer}
1586
- servicesData={servicesData}
1587
- setActiveRetailer={setActiveRetailer}
1588
- sendToFacilitator={sendToFacilitator}
1589
- approve={() => {
1590
- sendToFacilitator("A");
1591
- }}
1592
- reject={() => {
1593
- setShowRejectModal(true);
1594
- }}
1595
- showApproveRejectAll={
1596
- approveRejectAllButtons() && (auditorAssigned() || userAssigned())
1328
+ show={showRejectModal}
1329
+ customComponent={
1330
+ <TagAndInput
1331
+ inputType={"textarea"}
1332
+ inputId={"modal-message-box"}
1333
+ index={0}
1334
+ color={"white"}
1335
+ />
1597
1336
  }
1598
- showValidationButtons={
1599
- approveRejectButtons() && (auditorAssigned() || userAssigned())
1337
+ buttons={[
1338
+ <ButtonV2
1339
+ key={"btn-Cancelar"}
1340
+ type={"white"}
1341
+ label={"Cancelar"}
1342
+ size={12}
1343
+ onClick={() => {
1344
+ setShowRejectModal(false);
1345
+ }}
1346
+ />,
1347
+ <ButtonV2
1348
+ key={"btn-Aceptar"}
1349
+ type={"pink"}
1350
+ label={"Aceptar"}
1351
+ size={12}
1352
+ onClick={async () => {
1353
+ const elements = document.querySelectorAll(
1354
+ "#modal-message-box .ql-container .ql-editor > p",
1355
+ );
1356
+
1357
+ if (!elements || elements.length === 0) {
1358
+ console.error("Elemento no encontrado");
1359
+ return;
1360
+ }
1361
+
1362
+ const isMessageEmpty = Array.from(elements).every((el) => {
1363
+ const body = el.innerHTML;
1364
+ return (
1365
+ !body || body.replace(/<.*?\/?>/gm, "").trim() === ""
1366
+ );
1367
+ });
1368
+
1369
+ if (isMessageEmpty) {
1370
+ const container = document.querySelector(
1371
+ ".container-customComponent",
1372
+ );
1373
+ const existingAlert =
1374
+ container.querySelector(".alert-error");
1375
+
1376
+ if (!existingAlert) {
1377
+ const alert = document.createElement("div");
1378
+ alert.className = "alert-error";
1379
+ alert.style.cssText = `
1380
+ color: #d32f2f;
1381
+ background-color: #ffebee;
1382
+ border: 1px solid #ef5350;
1383
+ border-radius: 4px;
1384
+ padding: 12px 16px;
1385
+ margin-top: 10px;
1386
+ font-size: 14px;
1387
+ display: flex;
1388
+ align-items: center;
1389
+ gap: 8px;
1390
+ font-family: 'Roboto', sans-serif;
1391
+ text-align: center;
1392
+ `;
1393
+ alert.innerHTML = `<span>El mensaje no puede estar vacío.</span>`;
1394
+ container.appendChild(alert);
1395
+ }
1396
+ return;
1397
+ }
1398
+
1399
+ const fullMessage = Array.from(elements)
1400
+ .map((element) => element.innerHTML)
1401
+ .join("")
1402
+ .replace(/<br\s*\/?>/gi, "\n");
1403
+
1404
+ await createComment(fullMessage, token);
1405
+
1406
+ rejectAll
1407
+ ? sendBulkEvaluation("R")
1408
+ : sendSingleEvaluation("R");
1409
+ setShowRejectModal(false);
1410
+ }}
1411
+ />,
1412
+ ]}
1413
+ />
1414
+ )}
1415
+ {state.modal.show && (
1416
+ <GenericModal
1417
+ componentsArray={[
1418
+ state.modal.image && (
1419
+ <img key="1" src={state.modal.image} alt="modal icon" />
1420
+ ),
1421
+ state.modal.title && (
1422
+ <ScreenHeader
1423
+ key="2"
1424
+ headerType={"retailer-name-header"}
1425
+ text={state.modal.title}
1426
+ color={"white"}
1427
+ />
1428
+ ),
1429
+ state.modal.message && (
1430
+ <ScreenHeader
1431
+ key="3"
1432
+ headerType={"retailer-name-header"}
1433
+ text={state.modal.message}
1434
+ color={"white"}
1435
+ />
1436
+ ),
1437
+ state.modal.buttons && (
1438
+ <div key="4" style={{ marginTop: "16px" }}>
1439
+ {state.modal.buttons.map((button, index) => (
1440
+ <Button
1441
+ key={index}
1442
+ buttonType={button.buttonType}
1443
+ label={button.text}
1444
+ onClick={button.action}
1445
+ />
1446
+ ))}
1447
+ </div>
1448
+ ),
1449
+ ].filter(Boolean)}
1450
+ onClick={() =>
1451
+ dispatch({
1452
+ type: "SET_MODAL",
1453
+ payload: { show: false, title: "", message: "", image: null },
1454
+ })
1600
1455
  }
1601
- approveAll={() => validateAll("A")}
1602
- rejectAll={() => {
1603
- setShowRejectModal(true);
1604
- setValRejAll(true);
1605
- }}
1606
- isObservationVisible={isObservationVisible}
1607
- toggleObservation={toggleObservation}
1608
- // handleClickOutside={handleClickOutside}
1609
- hideObservation={hideObservation}
1610
1456
  />
1611
- <FullTabsMenu
1612
- tabsSections={tabsSections}
1613
- status={retailerStatus}
1614
- activeTab={activeTab}
1615
- setActiveTab={setActiveTab}
1616
- setImageLayout={setImageLayout}
1617
- downloadImages={downloadImages}
1618
- askToDeleteImages={askToDeleteImages}
1619
- assig={assig[activeTab]}
1620
- setAssignation={setAssignation}
1621
- isRetailer={isRetailer}
1622
- showSaveButton={auditorAssigned() || userAssigned()}
1623
- version={version}
1624
- desc={desc}
1625
- setDesc={setDesc}
1626
- fich={fich}
1627
- setFich={setFich}
1628
- imag={imag}
1629
- setImag={setImag}
1630
- updatedDescriptions={updatedDescriptions}
1631
- setUpdatedDescriptions={setUpdatedDescriptions}
1632
- updatedDatasheets={updatedDatasheets}
1633
- setUpdatedDatasheets={setUpdatedDatasheets}
1634
- images={images}
1635
- setImages={setImages}
1636
- selectedImages={selectedImages}
1637
- setSelectedImages={setSelectedImages}
1457
+ )}
1458
+ {showModal && (
1459
+ <ProductImageModal
1460
+ images={state.images_values}
1461
+ setShowModal={setShowModal}
1462
+ approveRejectButtons={canShowSingleEvaluationButtons()}
1463
+ sendToFacilitator={handleOnSendEvaluationToFacilitator}
1464
+ />
1465
+ )}
1466
+ {showVersionSelector && (
1467
+ <VersionSelector
1468
+ modalId={"version-selector"}
1469
+ articleId={state.product.id_article}
1470
+ setVersion={handleOnChangeProductVersion}
1471
+ companyName={state.product.company}
1472
+ currentVersion={state.product.version}
1638
1473
  setShowVersionSelector={setShowVersionSelector}
1639
- onClickSave={() => {
1640
- switch (activeTab) {
1641
- case "Descripción":
1642
- !saving &&
1643
- product?.description_status !== "NS" &&
1644
- saveDescriptions();
1645
- break;
1646
- case "Ficha técnica":
1647
- !saving &&
1648
- product?.datasheet_status !== "NS" &&
1649
- saveDatasheets();
1650
- break;
1651
- case "Imágenes":
1652
- !saving && product?.images_status !== "NS" && updateImages();
1653
- break;
1654
-
1655
- default:
1656
- break;
1657
- }
1658
- }}
1659
- canAssign={![7, 8].includes(user.id_role)}
1474
+ jwt={token}
1660
1475
  />
1661
- <div
1662
- className={
1663
- "services-information-container " +
1664
- (imageLayout && activeTab === "Imágenes" ? "image-services" : "")
1665
- }
1666
- >
1667
- {loading ? (
1668
- <Loading />
1669
- ) : (
1670
- <>
1671
- {!imageLayout &&
1672
- activeTab === "Imágenes" &&
1673
- product?.services?.images === 1 && (
1674
- <GalleryHeader
1675
- setSelectedImages={setSelectedImages}
1676
- checkAll={checkAll}
1677
- setCheckAll={setCheckAll}
1678
- shotThd={shotThd}
1679
- />
1680
- )}
1476
+ )}
1477
+ <HeaderTop
1478
+ setHeaderTop={setHeaderTop}
1479
+ auditableVersion={auditableVersion}
1480
+ setCompare={setCompare}
1481
+ isAuditor={[1, 6].includes(user.id_role)}
1482
+ withChat={location?.state?.withChat}
1483
+ chatType={location?.state?.chatType}
1484
+ productSelected={state.product}
1485
+ token={token}
1486
+ activeRetailer={{
1487
+ id: state.active_retailer?.id_retailer,
1488
+ name: state.active_retailer?.retailer,
1489
+ }}
1490
+ />
1491
+ <div className="data-container">
1492
+ <div className="image-data-panel">
1493
+ <ImagePreviewer
1494
+ activeImage={
1495
+ state.images_values.values[state.current_image] ?? {}
1496
+ }
1497
+ imagesArray={state.images_values || []}
1498
+ setActiveImage={handleOnChangeCurrentImage}
1499
+ setShowModal={handleOnShowModalGallery}
1500
+ />
1501
+ <ImageDataTable
1502
+ // states
1503
+ lists={state.images_values || []}
1504
+ activeImage={
1505
+ state.images_values?.values
1506
+ ? state.images_values?.values[state.current_image]
1507
+ : {}
1508
+ }
1509
+ assignationsImages={state.collaborator_assignations["Imágenes"]}
1510
+ imagesStatus={state.product?.images_status || "-"}
1511
+ retailerSelected={state.active_retailer?.id_retailer}
1512
+ isRetailer={isRetailer}
1513
+ version={state.product.version}
1514
+ shotThd={shotThd}
1515
+ setShowVersionSelector={setShowVersionSelector}
1516
+ // actions
1517
+ setImages={handleOnChangeCurrentImage}
1518
+ setAssignation={handleOnChangeAssignations} // No se usa?
1519
+ onClickSave={handleOnClickSaveImages}
1520
+ showSaveButton={isAuditorAssigned() || isUserAssignedToService()}
1521
+ />
1522
+ </div>
1681
1523
 
1682
- {activeTab === "Ficha técnica" &&
1683
- (product?.datasheet_status !== "NS" ? (
1684
- datasheets[0]?.data?.map((dataGroup, index) => (
1524
+ <div className="product-information">
1525
+ <FullProductNameHeader
1526
+ // states
1527
+ headerData={state.product}
1528
+ productObservation={observation}
1529
+ percent={state.active_percentage.percentagesGeneral.required}
1530
+ activeRetailer={{
1531
+ id: state.active_retailer?.id_retailer,
1532
+ name: state.active_retailer?.retailer,
1533
+ image: state.active_retailer?.image,
1534
+ }}
1535
+ servicesData={state.services_data ? state.services_data : null}
1536
+ isObservationVisible={isObservationVisible}
1537
+ // actions
1538
+ setActiveRetailer={handleOnChangeActiveRetailer}
1539
+ approve={handleOnApproveSingleService}
1540
+ approveAll={handleOnApproveAllServices}
1541
+ reject={handleOnRejectSingleService}
1542
+ rejectAll={handleOnRejectAllServices}
1543
+ toggleObservation={handleOnToggleObservation}
1544
+ hideObservation={handleOnHideObservation}
1545
+ // sendToFacilitator={handleOnSendEvaluationToFacilitator} // No se usa?
1546
+ // validations
1547
+ showApproveRejectAll={
1548
+ canShowBulkEvaluationButtons() ||
1549
+ isAuditorAssigned() ||
1550
+ isUserAssignedToService()
1551
+ }
1552
+ showValidationButtons={
1553
+ canShowSingleEvaluationButtons() &&
1554
+ (isAuditorAssigned() || isUserAssignedToService())
1555
+ }
1556
+ />
1557
+ <FullTabsMenu
1558
+ tabsSections={tabsSections}
1559
+ status={getStatusByCurrentServiceAndRetailer()}
1560
+ activeTab={state.active_tab}
1561
+ isRetailer={isRetailer}
1562
+ assig={state.collaborator_assignations[state.active_tab]}
1563
+ version={state.product.version}
1564
+ updatedDescriptions={state.updated_descriptions_inputs}
1565
+ updatedDatasheets={state.updated_datasheets_inputs}
1566
+ updatedImages={state.updated_images_values}
1567
+ images={state.services.images}
1568
+ // selectedImages={selectedImages} prop pasada asi: FullTabsMenu -> TabsMenu Pero en TabsMenu no se espera ninguna prop setSelectedImages
1569
+ // setters
1570
+ setActiveTab={handleOnChangeActiveTab}
1571
+ setImageLayout={handleOnToggleImageLayout}
1572
+ setUpdatedDescriptions={handleOnSetUpdatedDescriptions}
1573
+ setUpdatedDatasheets={handleOnSetUpdatedDatasheets}
1574
+ setAssignation={handleOnSetAssignation} // No se usa?
1575
+ setImages={handleOnSetImages} // No se usa?
1576
+ setShowVersionSelector={setShowVersionSelector}
1577
+ // setSelectedImages={setSelectedImages} prop pasada asi: FullTabsMenu -> TabsMenu Pero en TabsMenu no se espera ninguna prop setSelectedImages
1578
+ // actions
1579
+ downloadImages={handleOnDownloadImages}
1580
+ askToDeleteImages={handleOnAskToDeleteImages}
1581
+ showSaveButton={isAuditorAssigned() || isUserAssignedToService()}
1582
+ onClickSave={handleOnClickSave}
1583
+ // validations
1584
+ canAssign={![7, 8].includes(user.id_role)}
1585
+ />
1586
+ <div
1587
+ className={
1588
+ "services-information-container " +
1589
+ (imageLayout && state.active_tab === "Imágenes"
1590
+ ? "image-services"
1591
+ : "")
1592
+ }
1593
+ id="services-information-container"
1594
+ >
1595
+ {state.saving ? (
1596
+ <Loading />
1597
+ ) : (
1598
+ <>
1599
+ {state.active_tab === "Descripción" &&
1600
+ (state.product?.description_status !== "NS" ? (
1685
1601
  <InputGroup
1686
- key={index + "-" + activeRetailer.name}
1687
- articleId={product.article.id_article}
1688
- version={version}
1689
- activeSection={activeTab}
1690
- inputGroup={dataGroup}
1691
- dataInputs={datasheets[1]}
1692
- auditInputs={auditDatasheets[1]}
1693
- updatedDatasheets={updatedDatasheets}
1694
- setUpdatedDatasheets={setUpdatedDatasheets}
1602
+ activeSection={state.active_tab}
1603
+ inputGroup={state.descriptions_inputs[0]}
1604
+ updatedDescriptions={state.updated_descriptions_inputs}
1605
+ articleId={state.product?.id_article}
1606
+ version={state.product.version}
1607
+ auditInputGroup={auditDescriptions[0]}
1608
+ setUpdatedDescriptions={handleOnSetUpdatedDescriptions}
1609
+ dinamicHeight={true}
1695
1610
  compare={compare}
1696
1611
  />
1697
- ))
1698
- ) : (
1699
- <ScreenHeader
1700
- text={"No cuentas con este servicio"}
1701
- headerType={"input-name-header"}
1702
- />
1703
- ))}
1704
- {activeTab === "Descripción" &&
1705
- (product?.description_status !== "NS" ? (
1706
- <InputGroup
1707
- activeSection={activeTab}
1708
- inputGroup={descriptions[0]}
1709
- auditInputGroup={auditDescriptions[0]}
1710
- updatedDescriptions={updatedDescriptions}
1711
- setUpdatedDescriptions={setUpdatedDescriptions}
1712
- articleId={product?.article?.id_article}
1713
- version={version}
1714
- dinamicHeight={true}
1715
- compare={compare}
1716
- />
1717
- ) : (
1718
- <ScreenHeader
1719
- text={"No cuentas con este servicio"}
1720
- headerType={"input-name-header"}
1721
- />
1722
- ))}
1723
-
1724
- {activeTab === "Imágenes" &&
1725
- (product?.images_status !== "NS" ? (
1726
- <section className="container">
1727
- <div {...getRootProps({ className: "dropzone" })}>
1728
- <input {...getInputProps()} />
1729
- <aside>{thumbs()}</aside>
1612
+ ) : (
1613
+ <ScreenHeader
1614
+ text={"No cuentas con este servicio"}
1615
+ headerType={"input-name-header"}
1616
+ />
1617
+ ))}
1618
+ {state.active_tab === "Ficha técnica" &&
1619
+ (state.product?.datasheet_status !== "NS" ? (
1620
+ <>
1621
+ {state.datasheets_inputs[0]?.data?.map(
1622
+ (dataGroup, index) => (
1623
+ <InputGroup
1624
+ key={index + "-" + state.active_retailer.retailer}
1625
+ articleId={state.product.id_article}
1626
+ version={state.version}
1627
+ activeSection={state.active_tab}
1628
+ inputGroup={dataGroup}
1629
+ dataInputs={state.datasheets_inputs[1]}
1630
+ auditInputs={auditDatasheets[1]}
1631
+ updatedDatasheets={
1632
+ state.updated_datasheets_inputs
1633
+ }
1634
+ setUpdatedDatasheets={
1635
+ handleOnSetUpdatedDatasheets
1636
+ }
1637
+ compare={compare}
1638
+ />
1639
+ ),
1640
+ )}
1641
+ </>
1642
+ ) : (
1643
+ <ScreenHeader
1644
+ text={"No cuentas con este servicio"}
1645
+ headerType={"input-name-header"}
1646
+ />
1647
+ ))}
1648
+ {state.active_tab === "Imágenes" &&
1649
+ (state.product?.images_status !== "NS" ? (
1650
+ <>
1651
+ {!imageLayout && (
1652
+ <GalleryHeader
1653
+ checkAll={state.all_image_checked} // Determina si el checkbox "Seleccionar todo" está marcado
1654
+ setCheckAll={handleOnSetCheckAll} // Toggler de true o false para el checkbox "Seleccionar todo"
1655
+ setSelectedImages={handleOnSetSelectedImages}
1656
+ // shotThd={shotThd} // No se usa?
1657
+ />
1658
+ )}
1659
+ <section
1660
+ className="container"
1661
+ style={{ position: "relative" }}
1662
+ >
1663
+ <div
1664
+ {...getRootProps({
1665
+ className: `dropzone ${
1666
+ isDragActive ? "drag-active" : ""
1667
+ }`,
1668
+ })}
1669
+ >
1670
+ <input {...getInputProps()} />
1671
+ {isDragActive && (
1672
+ <div className="drag-overlay">
1673
+ <p>Suelta las imágenes aquí</p>
1674
+ </div>
1675
+ )}
1676
+ <aside>{thumbs()}</aside>
1677
+ </div>
1678
+ {state.images_values?.values.length === 0 && (
1679
+ <div
1680
+ style={{
1681
+ position: "absolute",
1682
+ top: "50%",
1683
+ left: "50%",
1684
+ transform: "translate(-50%, -50%)",
1685
+ textAlign: "center",
1686
+ padding: "40px",
1687
+ width: "80%",
1688
+ maxWidth: "500px",
1689
+ fontFamily: "Arial, sans-serif",
1690
+ }}
1691
+ >
1692
+ <p
1693
+ style={{
1694
+ fontSize: "18px",
1695
+ color: "#666",
1696
+ marginBottom: "16px",
1697
+ }}
1698
+ >
1699
+ Este producto no tiene imágenes
1700
+ </p>
1701
+ {isAuditorAssigned() ||
1702
+ isUserAssignedToService() ? (
1703
+ <p
1704
+ style={{
1705
+ fontSize: "14px",
1706
+ color: "#999",
1707
+ marginBottom: "20px",
1708
+ }}
1709
+ >
1710
+ Arrastra las imágenes aquí o{" "}
1711
+ <span
1712
+ onClick={open}
1713
+ style={{
1714
+ color: "#007bff",
1715
+ cursor: "pointer",
1716
+ textDecoration: "underline",
1717
+ }}
1718
+ >
1719
+ haz clic para abrir el explorador de
1720
+ archivos
1721
+ </span>
1722
+ </p>
1723
+ ) : null}
1724
+ </div>
1725
+ )}
1726
+ </section>
1727
+ </>
1728
+ ) : (
1729
+ <ScreenHeader
1730
+ text={"No cuentas con este servicio"}
1731
+ headerType={"input-name-header"}
1732
+ />
1733
+ ))}
1734
+ </>
1735
+ )}
1736
+ </div>
1737
+ {(isUserAssignedToService(state.active_tab) ||
1738
+ isAuditorAssigned()) &&
1739
+ state.product[`${getConceptByTab(state.active_tab)}_status`] !==
1740
+ "NS" && (
1741
+ <div className="commentary-box">
1742
+ {!state.comment ? (
1743
+ <div className="commentary">
1744
+ <TagAndInput
1745
+ label={"Caja de Comentario"}
1746
+ inputType={"textarea"}
1747
+ inputCols={80}
1748
+ inputRows={4}
1749
+ inputId={"commentary-box"}
1750
+ index={0}
1751
+ />
1752
+ <div className="buttons-box">
1753
+ <Button
1754
+ buttonType={"general-transparent-button"}
1755
+ label={"Enviar comentario"}
1756
+ onClick={handleOnSubmitComment}
1757
+ />
1730
1758
  </div>
1731
- </section>
1759
+ </div>
1732
1760
  ) : (
1733
- <ScreenHeader
1734
- text={"No cuentas con este servicio"}
1735
- headerType={"input-name-header"}
1736
- />
1737
- ))}
1738
- </>
1739
- )}
1740
- </div>
1741
- {/* {(userAssigned(activeTab) || auditorAssigned()) &&
1742
- product[`${getConcept(activeTab)}_status`] !== "NS" && (
1743
- <div className="commentary-box">
1744
- {[7, 8].includes(user.id_role) && (
1745
- <Button
1746
- buttonType={
1747
- evaluationFinished(
1748
- user.id_role,
1749
- activeTab,
1750
- statusArray
1751
- ) && requiredNull[activeTab] === 0
1752
- ? "general-green-button"
1753
- : "general-button-disabled"
1754
- }
1755
- label={"Enviar evaluación"}
1756
- onClick={() => sendToFacilitator()}
1757
- />
1758
- )}
1759
- </div>
1760
- )} */}
1761
-
1762
- {(userAssigned(activeTab) || auditorAssigned()) &&
1763
- product[`${getConcept(activeTab)}_status`] !== "NS" && (
1764
- <div className="commentary-box">
1765
- {!comment ? (
1766
- <div className="commentary">
1767
- <TagAndInput
1768
- label={"Caja de Comentario"}
1769
- inputType={"textarea"}
1770
- inputCols={80}
1771
- inputRows={4}
1772
- inputId={"commentary-box"}
1773
- index={0}
1774
- />
1775
- <div className="buttons-box">
1761
+ <div className="feedback-box">
1762
+ <Commentary
1763
+ comment={state.comment?.message}
1764
+ reviewed={crossComment}
1765
+ />
1776
1766
  <Button
1777
- buttonType={"general-transparent-button"}
1778
- label={"Enviar comentario"}
1779
- onClick={handleCommentSubmit}
1767
+ buttonType={"circular-button accept-button"}
1768
+ onClick={async () => {
1769
+ setCrossComment(true);
1770
+ commentRevised();
1771
+ }}
1780
1772
  />
1781
1773
  </div>
1782
- </div>
1783
- ) : (
1784
- <div className="feedback-box">
1785
- <Commentary
1786
- comment={comment?.message}
1787
- reviewed={crossComment}
1788
- />
1774
+ )}
1775
+
1776
+ <div className="action-buttons">
1789
1777
  <Button
1790
- buttonType={"circular-button accept-button"}
1791
- onClick={async () => {
1792
- setCrossComment(true);
1793
- commentRevised();
1794
- }}
1778
+ buttonType={"general-pink-button"}
1779
+ label={"Cambio de Estatus"}
1780
+ onClick={() => setOpenChangeStatusModal(true)}
1781
+ id="button-change-status"
1795
1782
  />
1783
+ {[7, 8].includes(user.id_role) && (
1784
+ <Button
1785
+ buttonType={
1786
+ isEvaluationFinished(
1787
+ user.id_role,
1788
+ state.active_tab,
1789
+ statusArray,
1790
+ ) &&
1791
+ state.missing_required_fields[state.active_tab] === 0
1792
+ ? "general-green-button"
1793
+ : "general-button-disabled"
1794
+ }
1795
+ label={"Enviar evaluación"}
1796
+ onClick={handleOnSendEvaluationToFacilitator}
1797
+ />
1798
+ )}
1796
1799
  </div>
1797
- )}
1798
- {[7, 8].includes(user.id_role) && (
1799
- <Button
1800
- buttonType={
1801
- evaluationFinished(
1802
- user.id_role,
1803
- activeTab,
1804
- statusArray
1805
- ) && requiredNull[activeTab] === 0
1806
- ? "general-green-button"
1807
- : "general-button-disabled"
1808
- }
1809
- label={"Enviar evaluación"}
1810
- onClick={() => sendToFacilitator()}
1811
- />
1812
- )}
1813
- </div>
1814
- )}
1800
+ </div>
1801
+ )}
1802
+ </div>
1815
1803
  </div>
1816
- </div>
1817
- {showModal && (
1818
- <ProductImageModal
1819
- images={images}
1820
- setShowModal={setShowModal}
1821
- sendToFacilitator={sendToFacilitator}
1822
- approveRejectButtons={approveRejectButtons()}
1823
- />
1824
- )}
1825
- {message.length > 0 && (
1826
- <GenericModal
1827
- buttonType={componentsArray.length > 0 && "delete-product"}
1828
- componentsArray={
1829
- componentsArray.length > 0
1830
- ? componentsArray
1831
- : [
1832
- <img key="1" src={succes} alt="success icon" />,
1833
- <ScreenHeader
1834
- key="2"
1835
- headerType={"retailer-name-header"}
1836
- text={message}
1837
- color={"white"}
1838
- />,
1839
- ]
1840
- }
1841
- onClick={() => setMessage("")}
1842
- />
1843
- )}
1844
- {showVersionSelector && (
1845
- <VersionSelector
1846
- modalId={"version-selector"}
1847
- articleId={product.article.id_article}
1848
- setVersion={setVersion}
1849
- companyName={product.article.company_name}
1850
- currentVersion={version}
1851
- setShowVersionSelector={setShowVersionSelector}
1852
- jwt={token}
1853
- />
1854
- )}
1855
- {showRejectModal && (
1856
- <Modal
1857
- title={`Agregar mensaje de rechazo para ${activeTab?.toLowerCase()}`}
1858
- show={showRejectModal}
1859
- customComponent={
1860
- <TagAndInput
1861
- inputType={"textarea"}
1862
- inputId={"modal-message-box"}
1863
- index={0}
1864
- color={"white"}
1865
- />
1866
- }
1867
- buttons={[
1868
- <ButtonV2
1869
- key={"btn-Cancelar"}
1870
- type={"white"}
1871
- label={"Cancelar"}
1872
- size={12}
1873
- onClick={() => {
1874
- setShowRejectModal(false);
1875
- }}
1876
- />,
1877
- <ButtonV2
1878
- key={"btn-Aceptar"}
1879
- type={"pink"}
1880
- label={"Aceptar"}
1881
- size={12}
1882
- onClick={async (e) => {
1883
- const elements = document.querySelectorAll(
1884
- "#modal-message-box .ql-container .ql-editor > p"
1885
- );
1886
-
1887
- if (!elements || elements.length === 0) {
1888
- console.error("Elemento no encontrado");
1889
- return;
1890
- }
1891
-
1892
- let brCounter = 0;
1893
-
1894
- elements.forEach((el) => {
1895
- const body = el.innerHTML;
1896
-
1897
- if (typeof body !== "string") {
1898
- console.log("El contenido de body no es una cadena", body);
1899
- isMessageEmpty = true;
1900
- return;
1901
- }
1902
-
1903
- if (!body || body.replace(/<.*?\/?>/gm, "").trim() === "") {
1904
- brCounter++;
1905
- }
1906
- });
1907
-
1908
- if (brCounter === elements.length) {
1909
- const container = document.querySelector(
1910
- ".container-customComponent"
1911
- );
1912
-
1913
- const existingAlert = container.querySelector(".alert-error");
1914
- if (existingAlert) {
1915
- return; // Si ya existe, no crear otra
1916
- }
1804
+ </Container>
1917
1805
 
1918
- // Crear alerta con estilos
1919
- const alert = document.createElement("div");
1920
- alert.className = "alert-error";
1921
- alert.style.cssText = `
1922
- color: #d32f2f;
1923
- background-color: #ffebee;
1924
- border: 1px solid #ef5350;
1925
- border-radius: 4px;
1926
- padding: 12px 16px;
1927
- margin-top: 10px;
1928
- font-size: 14px;
1929
- display: flex;
1930
- align-items: center;
1931
- gap: 8px;
1932
- font-family: 'Roboto', sans-serif;
1933
- text-align: center;
1934
- `;
1935
-
1936
- alert.innerHTML = `
1937
- <span>El mensaje no puede estar vacío.</span>
1938
- `;
1939
-
1940
- container.appendChild(alert);
1941
- return;
1942
- }
1943
-
1944
- let fullMessage = "";
1945
-
1946
- elements.forEach((element) => {
1947
- const body = element.innerHTML;
1948
-
1949
- fullMessage += body;
1950
- });
1951
-
1952
- fullMessage = fullMessage.replace(/<br\s*\/?>/gi, "\n");
1953
-
1954
- await createComment(e, fullMessage, activeTab);
1955
- valRejAll ? validateAll("R") : sendToFacilitator("R");
1956
- setMessage("Rechazado");
1957
- setShowRejectModal(false);
1958
- }}
1959
- />,
1960
- ]}
1961
- />
1962
- )}
1963
- <Modal
1964
- className="container-modalAlert"
1965
- show={modalAlert.show}
1966
- title={modalAlert.title}
1967
- message={modalAlert.message}
1968
- icon={"info"}
1969
- onClickBtnDefault={(event) => {
1970
- setModalAlert((prev) => ({
1971
- ...prev,
1972
- show: false,
1973
- errorInputMessage: false,
1974
- }));
1806
+ <ChangeStatusModal
1807
+ state={state}
1808
+ open={openChangeStatusModal}
1809
+ onClose={() => {
1810
+ setOpenChangeStatusModal(false);
1975
1811
  }}
1812
+ reloadData={loadData}
1813
+ token={token}
1976
1814
  />
1977
- </Container>
1815
+ </AiProductEditionProvider>
1816
+ );
1817
+ };
1818
+
1819
+ export const RetailerProductEdition = (props) => {
1820
+ return (
1821
+ <ProviderProductEditionProvider>
1822
+ <RetailerProductEditionView {...props} />
1823
+ </ProviderProductEditionProvider>
1978
1824
  );
1979
1825
  };