contentoh-components-library 21.5.92 → 21.5.93

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/components/atoms/GeneralButton/styles.js +1 -1
  2. package/dist/components/atoms/GeneralInput/index.js +54 -245
  3. package/dist/components/atoms/GeneralInput/styles.js +3 -7
  4. package/dist/components/atoms/InputFormatter/index.js +68 -223
  5. package/dist/components/atoms/InputFormatter/styles.js +4 -20
  6. package/dist/components/molecules/StatusAsignationInfo/index.js +1 -11
  7. package/dist/components/molecules/TabsMenu/index.js +12 -13
  8. package/dist/components/molecules/TagAndInput/index.js +24 -361
  9. package/dist/components/molecules/TagAndInput/styles.js +2 -2
  10. package/dist/components/organisms/FullProductNameHeader/index.js +22 -6
  11. package/dist/components/organisms/InputGroup/index.js +18 -22
  12. package/dist/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +337 -150
  13. package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.context.js +15 -15
  14. package/dist/components/pages/ProviderProductEdition/index.js +362 -368
  15. package/dist/components/pages/ProviderProductEdition/utils.js +0 -1
  16. package/dist/components/pages/RetailerProductEdition/RetailerProductEdition.stories.js +196 -179
  17. package/dist/components/pages/RetailerProductEdition/context/provider-product-edition.context.js +260 -59
  18. package/dist/components/pages/RetailerProductEdition/context/reducers/product.js +38 -50
  19. package/dist/components/pages/RetailerProductEdition/index.js +2234 -1716
  20. package/dist/components/pages/RetailerProductEdition/styles.js +2 -4
  21. package/dist/components/pages/RetailerProductEdition/utils.js +2 -251
  22. package/dist/contexts/AiProductEdition.js +158 -230
  23. package/package.json +2 -4
  24. package/src/components/atoms/GeneralButton/styles.js +0 -4
  25. package/src/components/atoms/GeneralInput/index.js +60 -237
  26. package/src/components/atoms/GeneralInput/styles.js +0 -81
  27. package/src/components/atoms/InputFormatter/index.js +51 -200
  28. package/src/components/atoms/InputFormatter/styles.js +0 -284
  29. package/src/components/molecules/StatusAsignationInfo/index.js +1 -9
  30. package/src/components/molecules/TabsMenu/index.js +11 -12
  31. package/src/components/molecules/TagAndInput/index.js +21 -286
  32. package/src/components/molecules/TagAndInput/styles.js +17 -59
  33. package/src/components/organisms/FullProductNameHeader/index.js +28 -4
  34. package/src/components/organisms/FullTabsMenu/index.js +1 -1
  35. package/src/components/organisms/InputGroup/index.js +4 -12
  36. package/src/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +202 -174
  37. package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx +14 -14
  38. package/src/components/pages/ProviderProductEdition/index.js +452 -453
  39. package/src/components/pages/ProviderProductEdition/utils.js +2 -2
  40. package/src/components/pages/RetailerProductEdition/RetailerProductEdition.stories.js +224 -201
  41. package/src/components/pages/RetailerProductEdition/index.js +1718 -1547
  42. package/src/components/pages/RetailerProductEdition/styles.js +2 -67
  43. package/src/components/pages/RetailerProductEdition/utils.js +0 -240
  44. package/dist/ai/utils/compare-strings.js +0 -45
  45. package/dist/components/organisms/ChangeStatusModal/index.js +0 -531
  46. package/dist/components/organisms/ChangeStatusModal/styles.js +0 -85
  47. package/dist/global-files/statusDictionary.js +0 -103
  48. package/src/ai/utils/compare-strings.js +0 -45
  49. package/src/assets/images/Icons/arrow.png +0 -0
  50. package/src/assets/images/Icons/cancel.png +0 -0
  51. package/src/assets/images/Icons/ia-icon.png +0 -0
  52. package/src/assets/images/Icons/loading.svg +0 -5
  53. package/src/assets/images/Icons/reload.png +0 -0
  54. package/src/components/atoms/RetailerSelector/RetailerSelector.stories.js +0 -10
  55. package/src/components/atoms/RetailerSelector/index.js +0 -3
  56. package/src/components/atoms/RetailerSelector/styles.js +0 -0
  57. package/src/components/organisms/ChangeStatusModal/index.jsx +0 -488
  58. package/src/components/organisms/ChangeStatusModal/styles.js +0 -333
  59. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.context.jsx +0 -575
  60. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.reducer.js +0 -62
  61. package/src/components/pages/RetailerProductEdition/context/reducers/active-state.js +0 -344
  62. package/src/components/pages/RetailerProductEdition/context/reducers/inputs.js +0 -155
  63. package/src/components/pages/RetailerProductEdition/context/reducers/product.js +0 -114
  64. package/src/components/pages/RetailerProductEdition/context/reducers/system.js +0 -60
  65. package/src/components/pages/RetailerProductEdition/index_old.js +0 -1979
  66. package/src/components/pages/RetailerProductEdition/stories/Auditor.stories.js +0 -101
  67. package/src/components/pages/RetailerProductEdition/stories/ImageEditor.stories.js +0 -115
  68. package/src/components/pages/RetailerProductEdition/stories/TextEditor.stories.js +0 -174
  69. package/src/contexts/AiProductEdition.jsx +0 -339
  70. package/src/global-files/statusDictionary.js +0 -103
@@ -1,286 +1,392 @@
1
- import { useEffect, useState } from "react";
2
-
3
- import axios from "axios";
4
- import { useDropzone } from "react-dropzone";
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";
5
10
  import { saveAs } from "file-saver";
6
-
11
+ import { Commentary } from "../../atoms/Commentary";
7
12
  import {
8
- ProviderProductEditionProvider,
9
- useProviderProductEdition,
10
- } from "./context/provider-product-edition.context";
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";
11
34
  import {
12
- normalizeProduct,
13
- getConceptByTab,
14
35
  getAuditVersion,
15
36
  getInputsData,
16
37
  createMessage,
17
38
  sendMessage,
18
- translateConcept,
19
- getStatusArrayByRole,
20
- getConceptsByRole,
21
- buildCollaboratorAssignations,
22
- calculateRequiredNull,
23
39
  } from "./utils";
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";
40
+ import { Modal } from "../../organisms/Modal";
37
41
  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
+ }
38
78
 
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";
43
-
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";
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
+ );
52
119
 
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";
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
+ };
59
130
 
60
- import { Container } from "./styles";
61
- import { Commentary } from "../../atoms/Commentary";
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
+ }
62
168
 
63
- import { AiProductEditionProvider } from "../../../contexts/AiProductEdition";
64
- import ChangeStatusModal from "../../organisms/ChangeStatusModal";
169
+ AWS.config.update({
170
+ accessKeyId: process.env.REACT_APP_KUTS3,
171
+ secretAccessKey: process.env.REACT_APP_AKUTS3,
172
+ });
65
173
 
66
- import { Skeleton, Box, Stack } from "@mui/material";
174
+ const myBucket = new AWS.S3({
175
+ params: { Bucket: S3_BUCKET },
176
+ region: REGION,
177
+ });
67
178
 
68
- const RetailerProductEditionView = ({
179
+ export const RetailerProductEdition = ({
69
180
  tabsSections,
70
181
  productSelected = {},
71
182
  user = {},
72
183
  token,
73
184
  location,
74
185
  }) => {
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);
92
- const [headerTop, setHeaderTop] = useState(0);
93
- const [shotThd, setShotThd] = useState(false);
94
- const [observation, setObservation] = useState();
95
- const [showRejectModal, setShowRejectModal] = useState(false);
96
- const [isObservationVisible, setObservationVisible] = useState(false);
186
+ const [activeTab, setActiveTab] = useState("Descripción");
187
+ const [activeImage, setActiveImage] = useState();
97
188
  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([]);
189
+ const [headerTop, setHeaderTop] = useState(0);
190
+ const [descriptions, setDescriptions] = useState([]);
191
+ const [datasheets, setDatasheets] = useState([]);
192
+ const [images, setImages] = useReducer(reducerImages, {});
105
193
  const [showModal, setShowModal] = useState(false);
106
-
107
- const [compare, setCompare] = useState(false);
108
- const { getRootProps, getInputProps, isDragActive, open } = useDropzone({
109
- accept: "image/*",
194
+ const [showRejectModal, setShowRejectModal] = useState(false);
195
+ const { getRootProps, getInputProps } = useDropzone({
196
+ accept: "image/*, video/mp4",
110
197
  noKeyboard: true,
111
198
  multiple: true,
112
199
  noClick: true,
113
200
  onDrop: (acceptedFiles) => {
201
+ const newImages = [];
114
202
  acceptedFiles.map((file) => {
115
203
  const reader = new FileReader();
116
204
  reader.fileName = file.name;
117
-
118
205
  reader.onload = async function (e) {
119
- const fileName = e.target.fileName;
120
- const fileExtension = fileName.split(".").pop();
121
- const fileDataURL = e.target.result;
122
-
206
+ const ext = e.srcElement.fileName.split(".");
123
207
  const img = new Image();
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
- };
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
+ }
143
262
  };
144
-
145
263
  reader.onerror = function (error) {
146
264
  console.log("dropzoneError: ", error);
147
265
  };
148
-
149
266
  reader.readAsDataURL(file);
150
267
  return file;
151
268
  });
152
269
  },
153
270
  });
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
+ });
154
327
 
155
- const [openChangeStatusModal, setOpenChangeStatusModal] = useState(false);
328
+ const [desc, setDesc] = useState([]);
329
+ const [fich, setFich] = useState([]);
330
+ const [imag, setImag] = useState([]);
156
331
 
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
- );
332
+ const [isObservationVisible, setObservationVisible] = useState(false);
176
333
 
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
- ));
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);
201
356
  };
202
357
 
203
- const initializeProduct = async () => {
204
- try {
205
- const product = JSON.parse(sessionStorage.getItem("productSelected"))
206
- ? JSON.parse(sessionStorage.getItem("productSelected"))
207
- : productSelected;
208
-
209
- const productNormalized = normalizeProduct(product);
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
- }
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);
261
364
  };
262
365
 
263
- // Setter del producto y carga inicial (optimizado para evitar cascada)
264
- useEffect(() => {
265
- initializeProduct();
366
+ useEffect(async () => {
367
+ const { id_article } = product?.article || {};
368
+ if (id_article)
369
+ await getAuditVersion(id_article, setAuditableVersion, token);
266
370
  }, []);
267
371
 
268
372
  const loadAuditableData = async () => {
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]);
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
+ }
284
390
  };
285
391
 
286
392
  useEffect(() => {
@@ -289,438 +395,682 @@ const RetailerProductEditionView = ({
289
395
  }
290
396
  }, [auditableVersion]);
291
397
 
292
- const loadData = async (enableLoading = true, versionSelected = null) => {
398
+ useEffect(() => {
399
+ checkAll && setSelectedImages(images.values);
400
+ }, [checkAll]);
401
+
402
+ const loadData = async () => {
293
403
  try {
294
- if (enableLoading) dispatch({ type: "SET_LOADING", payload: true });
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] });
295
464
 
296
- const { id_article, version, id_order } = state.product;
297
- const { category, id_category, id_retailer } = state.active_retailer;
465
+ if (services[2]?.values?.length > 0) setActiveImage(0);
298
466
 
299
- // Payloads y headers
300
467
  const data = [
301
468
  {
302
- id_article,
303
- version: versionSelected ? versionSelected : version,
469
+ id_article: product?.article?.id_article,
304
470
  relations: [
305
471
  {
306
- id_retailer,
307
- id_category,
472
+ id_retailer: retailer_id,
473
+ id_category: category_id,
308
474
  },
309
475
  ],
476
+ version: product.version,
310
477
  },
311
478
  ];
312
479
 
313
480
  const headers = { Authorization: token };
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
- })),
481
+ getPercentage({ data, headers }).then((res) => {
482
+ setPercentages(JSON.parse(res?.[0]?.body));
369
483
  });
370
484
 
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
- });
485
+ setImages({ action: "orderImages", retailerId: activeRetailer.id });
399
486
 
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
- }
487
+ setLoading(false);
472
488
  } catch (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 });
489
+ setLoading(false);
490
+ console.log(error);
477
491
  }
478
492
  };
479
493
 
480
494
  useEffect(() => {
481
- if (state.active_retailer) {
482
- loadData();
483
- }
484
- }, [state.active_retailer]);
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;
485
506
 
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
- }
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);
492
514
  };
493
515
 
494
- const handleOnShowModalGallery = () => {
495
- dispatch({
496
- type: "SET_ACTIVE_TAB",
497
- payload: "Imágenes",
498
- });
499
- setShowModal(true);
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];
500
523
  };
501
524
 
502
- const handleOnChangeActiveRetailer = (retailer) => {
503
- dispatch({
504
- type: "SET_ACTIVE_RETAILER",
505
- payload: retailer,
506
- });
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);
507
548
  };
508
549
 
509
- const handleOnChangeActiveTab = (tab) => {
510
- dispatch({
511
- type: "SET_ACTIVE_TAB",
512
- payload: tab,
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
+ },
513
626
  });
514
627
  };
515
628
 
516
- const handleOnToggleImageLayout = () => {
517
- setImageLayout(!imageLayout);
518
- };
629
+ useEffect(() => {
630
+ loadAssignations(product);
631
+ }, [userGroups]);
519
632
 
520
- const handleOnToggleObservation = () => {
521
- setObservationVisible(!isObservationVisible);
522
- };
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]);
523
654
 
524
- const handleOnHideObservation = () => {
525
- setObservationVisible(false);
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
+ ));
526
698
  };
527
699
 
528
- const isAuditorAssigned = () => {
529
- return state.product?.id_auditor === user?.id_user;
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
+ );
731
+
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
+ }
530
742
  };
531
743
 
532
- const isUserAssignedToService = (tab, rol) => {
533
- const concept = getConceptByTab(state.active_tab);
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
+ }
785
+ };
534
786
 
535
- const allowedRoles = [1, 4, 5, 6, 7, 8];
536
- const validUser = allowedRoles.includes(user?.id_role);
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
+ });
537
807
 
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";
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);
544
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
+ );
545
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);
928
+ }
929
+ }
930
+ }, [dataImages, imagesUploaded]);
546
931
 
547
- const isAdmin = user.id_role === 1;
548
- const idUserAssigned = state.product?.[`id_${concept}_${rol}`];
549
- const isAssigned = idUserAssigned === user.id_user;
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
+ };
550
975
 
551
- return isAdmin || (isAssigned && validUser);
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
+ }
552
985
  };
553
986
 
554
- const canShowSingleEvaluationButtons = (action) => {
555
- let concept = getConceptByTab(action || state.active_tab);
987
+ const approveRejectButtons = (action) => {
988
+ let concept = getConcept(action || activeTab);
556
989
 
557
- const retailerStatus = state.services_data
558
- ?.find(
990
+ const retailerStatus = servicesData
991
+ .find(
559
992
  (srv) =>
560
- srv.id_retailer === state.active_retailer?.id_retailer &&
561
- srv.service === concept,
993
+ srv.id_retailer === activeRetailer?.id && srv.service === concept
562
994
  )
563
995
  ?.status?.replace(/.*\//, "");
564
996
 
997
+ //sessionStorage product
565
998
  const adminFacilitatorCanEvaluate =
566
999
  retailerStatus === "IE" && [1, 4, 5].includes(user.id_role);
567
1000
  const adminAuditorCanEvaluate =
568
1001
  ["AC", "RP", "RCA", "SAC"].includes(retailerStatus) &&
569
1002
  [1, 6].includes(user.id_role);
570
-
571
1003
  return adminFacilitatorCanEvaluate || adminAuditorCanEvaluate;
572
1004
  };
573
1005
 
574
- const canShowBulkEvaluationButtons = () => {
575
- const concepts = getConceptsByRole(user.id_role);
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;
576
1015
 
577
- const services = state.services_data.filter(({ service }) =>
578
- concepts.includes(service),
1016
+ default:
1017
+ concepts = ["description", "datasheet", "images"];
1018
+ break;
1019
+ }
1020
+
1021
+ const services = servicesData.filter(({ service }) =>
1022
+ concepts.includes(service)
579
1023
  );
580
1024
 
581
1025
  const adminFacilitatorCanEvaluate =
582
1026
  services.every((srv) => srv?.status?.replace(/.*\//, "") === "IE") &&
583
1027
  [1, 4, 5].includes(user.id_role);
584
1028
 
1029
+ //sessionStorage product
585
1030
  const adminAuditorCanEvaluate =
586
- state.services_data.every((srv) =>
587
- ["AC", "RP", "RCA"].includes(srv?.status?.replace(/.*\//, "")),
1031
+ servicesData.every((srv) =>
1032
+ ["AC", "RP", "RCA"].includes(srv?.status?.replace(/.*\//, ""))
588
1033
  ) && [1, 6].includes(user.id_role);
589
1034
  return adminFacilitatorCanEvaluate || adminAuditorCanEvaluate;
590
1035
  };
591
1036
 
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) {
618
- case "Descripción":
619
- await saveDescriptions(token);
620
- break;
1037
+ const getSectionIcon = () => {
1038
+ switch (activeTab) {
621
1039
  case "Ficha técnica":
622
- await saveDatasheets(token);
1040
+ setIcon(attributesSent);
1041
+ break;
1042
+ case "Descripción":
1043
+ setIcon(descriptionSent);
623
1044
  break;
624
1045
  case "Imágenes":
625
- await updateImages(token);
1046
+ setIcon(imagesSent);
626
1047
  break;
627
1048
  default:
628
1049
  break;
629
1050
  }
630
-
631
- await loadData(false);
632
- };
633
-
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
1051
  };
693
1052
 
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
-
1053
+ const sendToFacilitator = async (result) => {
1054
+ if (!loading) setLoading(true);
718
1055
  try {
719
- //Esto solo se ejecutará cuando se aprueba o rechaza desde el modal superior derecho
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;
720
1072
  if (result) {
721
1073
  data.result = result;
722
- data.retailerId = retailerId;
723
-
724
1074
  res = await axios.put(
725
1075
  `${process.env.REACT_APP_EVALUATION_ENDPOINT}`,
726
1076
  data,
@@ -728,285 +1078,254 @@ const RetailerProductEditionView = ({
728
1078
  headers: {
729
1079
  Authorization: token,
730
1080
  },
731
- },
732
- );
733
-
734
- const newStatuses = JSON.parse(res.data.body);
735
- const serviceStatus =
736
- newStatuses.newServiceStatus[articleId][`${concept}Status`];
737
-
738
- // Actualizar el producto con los nuevos estados
739
- const updatedStatusByRetailer = state.product.statusByRetailer.map(
740
- (item) =>
741
- item.retailer_id === retailerId && item.service === concept
742
- ? { ...item, status: serviceStatus }
743
- : item,
1081
+ }
744
1082
  );
745
-
746
- const updatedProduct = {
747
- ...state.product,
748
- statusByRetailer: updatedStatusByRetailer,
749
- };
750
-
751
- dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
752
-
753
- // Mostrar modal de éxito
754
- dispatch({
755
- type: "SET_MODAL",
756
- payload: {
757
- show: true,
758
- title: "",
759
- message:
760
- result === "A" ? "Aprobado con éxito" : "Rechazado con éxito",
761
- image: successIcon,
762
- },
763
- });
764
- } else { //Caso del botón "Enviar evaluación"
765
-
766
- //Se construye el mensaje y se actualiza el estatus
767
-
1083
+ getServices();
1084
+ } else {
768
1085
  const specialistDone = ["RC", "RA", "CA"].includes(evalStatus);
769
1086
 
770
1087
  if (specialistDone) {
771
1088
  message = `${activeTab} enviada a facilitador`;
772
- icon =
773
- activeTab === "Descripción"
774
- ? descriptionSent
775
- : activeTab === "Ficha técnica"
776
- ? attributesSent
777
- : imagesSent;
1089
+ getSectionIcon();
778
1090
  } else if (["IE", "AC", "RP", "RCA"].includes(evalStatus)) {
779
1091
  message = "Evaluación enviada";
780
- icon = successIcon;
1092
+ getSectionIcon();
781
1093
  }
782
-
783
1094
  res = await axios.put(`${process.env.REACT_APP_SEND_EVAL}`, data, {
784
1095
  headers: {
785
1096
  Authorization: token,
786
1097
  },
787
1098
  });
788
1099
  }
789
-
790
1100
  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
+ );
791
1111
 
792
- const { newStatus, newOrderStatus, newArticleStatus } = JSON.parse( res.data.body );
793
-
794
- // const retailers = state.product.categoryRetailer.map((r) => ({
795
- // id: r.id_retailer,
796
- // name: r.retailerName,
797
- // }));
798
- // const messageToChat = createMessage(
799
- // retailers,
800
- // retailerId,
801
- // evalStatus,
802
- // newStatus,
803
- // activeTab,
804
- // );
805
-
806
- // const messageData = {
807
- // paramsBody: {
808
- // id: articleId,
809
- // version: state.product.version,
810
- // items: [{ type: "status", value: messageToChat }],
811
- // retailerId: retailerId,
812
- // status: state.product.status,
813
- // },
814
- // paramsHeader: { Authorization: token },
815
- // };
816
- // await sendMessage(messageData);
817
-
818
- const updatedProduct = {
819
- ...state.product,
820
- [`${concept}_status`]: newStatus,
821
- // actualizar el status es statusByRetailer
822
- statusByRetailer: state.product.statusByRetailer.map((item) => {
823
- if (item.retailer_id === retailerId && item.service === concept) {
824
- return { ...item, status: newStatus };
825
- }
826
- return item;
827
- }),
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 },
828
1121
  };
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
+ };
829
1135
 
830
- //Actualizamos el services_data en el servicio específico del retailer actual
831
-
832
- const updatedServicesData = state.services_data?.map(service => ({
833
- ...service,
834
- status: service?.id_retailer === retailerId && service?.service === concept ? newStatus : service?.status
835
- }));
836
-
837
- const sessionProduct = sessionStorage.getItem("productSelected");
838
-
839
- if (sessionProduct) {
840
- const productInSession = JSON.parse(sessionProduct);
841
- const updatedProductInSession = {
842
- ...productInSession,
843
- [`${concept}_status`]: newStatus,
844
- statusByRetailer: state.product.statusByRetailer.map((item) => {
845
- if (item.retailer_id === retailerId && item.service === concept) {
846
- return { ...item, status: newStatus };
847
- }
848
- return item;
849
- }),
850
- };
851
- sessionStorage.setItem(
852
- "productSelected",
853
- JSON.stringify(updatedProductInSession),
854
- );
855
- }
1136
+ const userAssigned = (tab, rol) => {
1137
+ let concept = getConcept(activeTab);
856
1138
 
857
- if (message) {
858
- dispatch({
859
- type: "SET_MODAL",
860
- payload: {
861
- show: true,
862
- title: "",
863
- message: message,
864
- image: successIcon,
865
- },
866
- });
867
- }
1139
+ const allowedRoles = [1, 4, 5, 6, 7, 8];
1140
+ const validUser = allowedRoles.includes(user?.id_role);
868
1141
 
869
- dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
870
- dispatch({ type: "SET_SERVICES_DATA", payload: updatedServicesData });
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;
871
1152
  }
872
- } catch (error) {
873
- console.error("Error sending evaluation:", error);
874
- dispatch({
875
- type: "SET_MODAL",
876
- payload: {
877
- show: true,
878
- title: "Error",
879
- message: "Hubo un error al procesar la evaluación",
880
- image: errorIcon,
881
- },
882
- });
883
- } finally {
884
- dispatch({ type: "SET_SAVING", payload: false });
885
1153
  }
1154
+
1155
+ return (
1156
+ user.id_role === 1 ||
1157
+ (product.article[`id_${concept}_${rol}`] === user.id_user && validUser)
1158
+ );
886
1159
  };
887
1160
 
888
- const handleOnApproveAllServices = async () => {
889
- await sendBulkEvaluation("A");
1161
+ const auditorAssigned = () => {
1162
+ return product?.article[`id_auditor`] === user.id_user;
890
1163
  };
891
1164
 
892
- const handleOnRejectAllServices = () => {
893
- setRejectAll(true);
894
- setShowRejectModal(true);
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([]);
895
1194
  };
896
1195
 
897
- const sendBulkEvaluation = async (result) => {
898
- dispatch({ type: "SET_SAVING", payload: true });
1196
+ const getRequired = (services) => {
899
1197
  try {
900
- const evaluationArray = [];
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
+ );
901
1212
 
902
- const conceptArray = getConceptsByRole(user.id_role);
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
+ });
903
1228
 
904
- state.services_data?.forEach((ret, idx) => {
905
- const { service: retailer_service, id_retailer, status: status_retailer_service } = ret;
1229
+ objetcTemp["Ficha técnica"] = dsInputsRequired.length;
906
1230
 
907
- const lastStatusLevel = status_retailer_service.replace(/.*\//, "");
1231
+ const regex = /(<\/?p>)|(<\/?strong>)|(<br>)/gm;
1232
+ descriptionsServicesArray.forEach((description) => {
1233
+ if (description.id != activeRetailer.id) return;
908
1234
 
909
- if (conceptArray.includes(retailer_service) && statusArray?.includes(lastStatusLevel)) {
910
- const data = {
911
- articleId: state.product.id_article,
912
- orderId: state.product.id_order,
913
- concept: retailer_service,
914
- result,
915
- evalStatus: status_retailer_service,
916
- retailerId: id_retailer,
917
- };
1235
+ const [requested] = servicesData.filter(
1236
+ (srv) =>
1237
+ srv.id_retailer === description.id &&
1238
+ srv.service === getConcept(activeTab)
1239
+ );
918
1240
 
919
- evaluationArray.push(
920
- axios.put(`${process.env.REACT_APP_EVALUATION_ENDPOINT}`, data, {
921
- headers: {
922
- Authorization: token,
923
- },
924
- }),
925
- );
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
+ });
926
1250
  }
927
1251
  });
928
1252
 
929
- await Promise.all(evaluationArray);
1253
+ objetcTemp["Descripción"] = desInputsRequired;
930
1254
 
931
- const status = `${result}A`;
1255
+ const retailersRequested = [];
932
1256
 
933
- // Actualizar el statusByRetailer para todos los retailers y conceptos
934
- const updatedStatusByRetailer = state.product.statusByRetailer.map(
935
- (item) =>
936
- conceptArray.includes(item.service) ? { ...item, status } : item,
1257
+ services[2]?.retailerMandatories?.forEach((retMan) =>
1258
+ retMan.forEach((rm) => retailersRequested.push(rm))
937
1259
  );
938
1260
 
939
- // Actualizar el producto con los nuevos estados
940
- const updatedProduct = {
941
- ...state.product,
942
- article_status: status,
943
- status: status,
944
- datasheet_status:
945
- state.product.datasheet_status === "NA" ? "NA" : status,
946
- description_status:
947
- state.product.description_status === "NA" ? "NA" : status,
948
- images_status: state.product.images_status === "NA" ? "NA" : status,
949
- statusByRetailer: updatedStatusByRetailer,
950
- };
951
-
952
- dispatch({
953
- type: "SET_PRODUCT",
954
- payload: updatedProduct,
955
- });
956
-
957
- const updatedServicesData = state.services_data.map(service => conceptArray.includes(service.service) ? { ...service, status: status } : service);
958
-
959
- dispatch({
960
- type: "SET_SERVICES_DATA",
961
- payload: updatedServicesData,
962
- });
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
+ );
963
1271
 
964
- // sessionStorage.setItem(
965
- // "productSelected",
966
- // JSON.stringify(updatedProduct),
967
- // );
968
- // sessionStorage.setItem(
969
- // "productEdit",
970
- // JSON.stringify({
971
- // ArticleId: updatedProduct.id_article,
972
- // idCategory: updatedProduct.id_category,
973
- // product: updatedProduct,
974
- // }),
975
- // );
976
-
977
- // Mostrar modal de éxito
978
- dispatch({
979
- type: "SET_MODAL",
980
- payload: {
981
- show: true,
982
- title: "",
983
- message:
984
- result === "A"
985
- ? "Todos los servicios aprobados con éxito"
986
- : "Todos los servicios rechazados con éxito",
987
- image: successIcon,
988
- },
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
+ );
989
1278
  });
1279
+ objetcTemp["Imágenes"] = requiredCounter;
1280
+ setRequiredNull(objetcTemp);
990
1281
  } catch (error) {
991
- console.error("Error in bulk evaluation:", error);
992
- } finally {
993
- dispatch({ type: "SET_SAVING", payload: false });
1282
+ console.log(error);
994
1283
  }
995
1284
  };
996
1285
 
997
- const handleOnChangeAssignations = async (assignationType, assignationId) => {
998
- const concept = getConceptByTab(state.active_tab);
1286
+ useEffect(() => {
1287
+ setComment(comments[activeTab]);
1288
+ }, [activeTab]);
1289
+
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
+ };
1302
+
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;
1312
+
1313
+ default:
1314
+ concept = "description";
1315
+ break;
1316
+ }
1317
+ const productTemp = product;
1318
+ productTemp.article[`id_${concept}_${assignationType}`] = assignationId;
999
1319
  const data = {
1000
1320
  articleList: [
1001
1321
  {
1002
- orderId: state.product.id_order,
1003
- articleId: state.product.id_article,
1322
+ orderId: product.orderId,
1323
+ articleId: product?.article?.id_article,
1004
1324
  },
1005
1325
  ],
1006
1326
  concept: concept,
1007
1327
  userId: assignationId,
1008
1328
  };
1009
-
1010
1329
  await axios({
1011
1330
  method: "post",
1012
1331
  url: process.env.REACT_APP_ASSIGNATIONS_ENDPOINT,
@@ -1015,794 +1334,646 @@ const RetailerProductEditionView = ({
1015
1334
  Authorization: token,
1016
1335
  },
1017
1336
  });
1018
-
1019
- const updatedProduct = {
1020
- ...state.product,
1021
- [`id_${concept}_${assignationType}`]: assignationId,
1022
- };
1023
-
1024
- dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
1025
- };
1026
-
1027
- const handleOnSetAssignation = () => {
1028
- console.log("handleOnSetAssignation - pendiente de implementar");
1029
- };
1030
-
1031
- const handleOnSetImages = () => {
1032
- console.log("handleOnSetImages - pendiente de implementar");
1033
- };
1034
-
1035
- const handleOnSetCheckAll = (isChecked) => {
1036
- dispatch({ type: "TOGGLE_CHECK_ALL_IMAGES", payload: isChecked });
1037
- };
1038
-
1039
- const handleOnSetSelectedImages = () => {
1040
- /*
1041
- Esta función no se utiliza actualmente.
1042
-
1043
- En la versión anterior se usaban dos funciones distintas para el checkbox
1044
- "Seleccionar todo":
1045
- - Una para cambiar el valor booleano del checkbox.
1046
- - Otra para actualizar el array de imágenes seleccionadas.
1047
-
1048
- En la versión actual se unificó la lógica en una sola función,
1049
- `handleOnSetCheckAll`, que recibe el valor `true` o `false` del checkbox
1050
- "Seleccionar todo" y, a partir de este, agrega o elimina todas las imágenes
1051
- del array de imágenes seleccionadas.
1052
- */
1053
- console.log("setSelectedImages");
1054
- };
1055
- const handleOnSubmitComment = async (e) => {
1056
- e.preventDefault();
1057
- const commentText = document.querySelector(
1058
- "#commentary-box .ql-container .ql-editor > p",
1059
- ).innerHTML;
1060
- try {
1061
- await createComment(commentText, token);
1062
-
1063
- dispatch({
1064
- type: "SET_MODAL",
1065
- payload: {
1066
- show: true,
1067
- title: "",
1068
- message: "Comentario guardado con éxito",
1069
- image: successIcon,
1070
- },
1071
- });
1072
- } catch (error) {
1073
- console.error("Error saving comment:", error);
1074
- dispatch({
1075
- type: "SET_MODAL",
1076
- payload: {
1077
- show: true,
1078
- title: "Error",
1079
- message: "Hubo un error al guardar el comentario",
1080
- image: errorIcon,
1081
- },
1082
- });
1083
- }
1337
+ loadAssignations(productTemp);
1338
+ sessionStorage.setItem("productSelected", JSON.stringify(productTemp));
1084
1339
  };
1085
1340
 
1086
- const handleOnSendEvaluationToFacilitator = () => {
1087
- sendSingleEvaluation();
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
+ });
1088
1359
  };
1089
1360
 
1090
- const isEvaluationFinished = (id_rol, tab, statusArray) => {
1091
- const servicesData = state.services_data;
1092
- const activeRetailer = state.active_retailer;
1093
- const product = state.product;
1094
- const concept = getConceptByTab(tab);
1095
-
1096
- const servicesByTab = servicesData?.filter(
1097
- (serv) => serv.service === concept,
1098
- );
1099
-
1100
- const statusBySelectedRetailerService = servicesByTab
1101
- ?.find((serv) => serv.id_retailer === activeRetailer?.id_retailer)
1102
- ?.status?.replace(/.*\//, "");
1361
+ const deleteImages = async () => {
1362
+ setLoading(true);
1363
+ const { values } = images;
1364
+ const imgsInBack = [];
1103
1365
 
1104
- const currentProducStatus = product?.[`${concept}_status`]?.replace(
1105
- /.*\//,
1106
- "",
1107
- );
1366
+ selectedImages.forEach((selectedImg) => {
1367
+ if (selectedImg.id) imgsInBack.push(selectedImg);
1368
+ });
1108
1369
 
1109
- const unvalidated = ["IE", "CA"].includes(currentProducStatus);
1110
- const auditorUnvalidated = !["RA", "AA", "ACA", "AP"].includes(
1111
- currentProducStatus,
1370
+ const imgsLeft = values.filter(
1371
+ (value) => selectedImages.indexOf(value) === -1
1112
1372
  );
1113
1373
 
1114
- let result;
1115
- switch (id_rol) {
1116
- case 7:
1117
- case 8:
1118
-
1119
- const canSendEvaluation = statusArray.includes(statusBySelectedRetailerService);
1120
-
1121
- // const conditionTwo = statusArray.includes(product?.status);
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
+ }
1391
+ }
1122
1392
 
1123
- // const conditionThree =
1124
- // servicesByTab.filter((service) =>
1125
- // statusArray.includes(service.status?.replace(/.*\//, "")),
1126
- // ).length === servicesByTab.length;
1393
+ setImages({
1394
+ action: "deleteImage",
1395
+ selectedImages,
1396
+ });
1127
1397
 
1128
- // return (canSendEvaluation && conditionTwo) || conditionThree;
1398
+ getRequired([
1399
+ services[0],
1400
+ services[1],
1401
+ { ...services[2], values: imgsLeft },
1402
+ ]);
1129
1403
 
1130
- return canSendEvaluation;
1404
+ setLoading(false);
1131
1405
 
1132
- case 4:
1133
- case 5:
1134
- const filteredSrv45 = servicesByTab?.filter((serv) =>
1135
- statusArray.includes(serv?.status),
1136
- );
1137
- const condition45 =
1138
- unvalidated &&
1139
- ["CA", "IE"].includes(product?.status) &&
1140
- filteredSrv45?.length === servicesByTab?.length;
1141
- return condition45;
1142
- case 6:
1143
- const statusInArray = statusArray.includes(product?.status);
1144
- const allSrvValid = srv?.every((serv) =>
1145
- ["RA", "AA", "AP", "ACA"].includes(serv?.status),
1146
- );
1147
- const condition6 = statusInArray && allSrvValid && auditorUnvalidated;
1148
- return condition6;
1149
- default:
1150
- console.log("Default case - returning false");
1151
- return false;
1152
- }
1406
+ setMessage("");
1407
+ setComponentsArray([]);
1153
1408
  };
1154
1409
 
1155
- const handleOnChangeProductVersion = (version) => {
1156
- dispatch({
1157
- type: "SET_PRODUCT_VERSION",
1158
- payload: version,
1159
- });
1160
-
1161
- loadData(true, version);
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
+ }
1162
1437
  };
1163
1438
 
1164
- const ProductEditionSkeleton = () => {
1165
- return (
1166
- <Container headerTop={0}>
1167
- <Box
1168
- px={1.5}
1169
- py={1}
1170
- pb={0}
1171
- display="flex"
1172
- alignItems="center"
1173
- justifyContent="space-between"
1174
- >
1175
- <Skeleton variant="text" width={300} height={40} />
1176
- <Stack direction="row" spacing={2}>
1177
- <Skeleton variant="circular" width={32} height={32} />
1178
- </Stack>
1179
- </Box>
1180
-
1181
- <div
1182
- className="data-container"
1183
- style={{ display: "flex", gap: "8px", padding: "0 12px 12px" }}
1184
- >
1185
- <div
1186
- className="image-data-panel"
1187
- style={{
1188
- flex: "0 0 320px",
1189
- display: "flex",
1190
- flexDirection: "column",
1191
- gap: "20px",
1192
- }}
1193
- >
1194
- <Skeleton variant="rounded" width="100%" height={400} />
1195
-
1196
- <Stack direction="row" spacing={2} justifyContent="center">
1197
- {[1, 2, 3, 4].map((item) => (
1198
- <Skeleton key={item} variant="rounded" width={60} height={80} />
1199
- ))}
1200
- </Stack>
1201
-
1202
- <Box mt={2}>
1203
- {[1, 2, 3, 4].map((item) => (
1204
- <Box
1205
- key={item}
1206
- display="flex"
1207
- justifyContent="space-between"
1208
- py={1}
1209
- borderBottom="1px solid #f0f0f0"
1210
- >
1211
- <Skeleton variant="text" width="40%" />
1212
- <Skeleton variant="text" width="20%" />
1213
- </Box>
1214
- ))}
1215
- </Box>
1216
- </div>
1439
+ const getRetailerStatus = (servicesData, tab) => {
1440
+ const arr = servicesData?.slice();
1441
+ let concept = getConcept(tab);
1217
1442
 
1218
- <div
1219
- className="product-information"
1220
- style={{
1221
- flex: 1,
1222
- display: "flex",
1223
- flexDirection: "column",
1224
- gap: "14px",
1225
- }}
1226
- >
1227
- <Box>
1228
- <Skeleton variant="text" width="80%" height={50} />
1229
- <Stack direction="row" spacing={2} alignItems="center">
1230
- <Skeleton variant="rounded" width={180} height={24} />
1231
- <Skeleton variant="rounded" width={300} height={24} />
1232
- </Stack>
1233
- </Box>
1234
-
1235
- <Box display="flex" gap={4} pb={1}>
1236
- <Skeleton variant="text" width={140} height={45} />
1237
- <Skeleton variant="text" width={140} height={45} />
1238
- <Skeleton variant="text" width={140} height={45} />
1239
- </Box>
1240
-
1241
- <Box display="flex" flexDirection="column" gap={4}>
1242
- {[1, 2, 3].map((item) => (
1243
- <Box key={item}>
1244
- <Box
1245
- display="flex"
1246
- justifyContent="space-between"
1247
- alignItems="center"
1248
- mb={1}
1249
- >
1250
- <Skeleton variant="text" width="30%" height={24} />
1251
- <Skeleton
1252
- variant="rounded"
1253
- width={175}
1254
- height={25}
1255
- sx={{ borderRadius: "7.5px" }}
1256
- />
1257
- </Box>
1258
- <Skeleton variant="rounded" width="100%" height={70} />
1259
- <Box display="flex" justifyContent="flex-end" mt={0.5}>
1260
- <Skeleton variant="text" width={60} />
1261
- </Box>
1262
- </Box>
1263
- ))}
1264
- </Box>
1265
-
1266
- <Box mt="auto" pt={4}>
1267
- <Skeleton variant="rounded" width="100%" height={120} />
1268
- <Box display="flex" justifyContent="flex-end" gap={2} mt={2}>
1269
- <Skeleton
1270
- variant="rounded"
1271
- width={180}
1272
- height={45}
1273
- sx={{ borderRadius: "24px" }}
1274
- />
1275
- <Skeleton
1276
- variant="rounded"
1277
- width={180}
1278
- height={45}
1279
- sx={{ borderRadius: "24px" }}
1280
- />
1281
- </Box>
1282
- </Box>
1283
- </div>
1284
- </div>
1285
- </Container>
1443
+ let retailerService = {};
1444
+ [retailerService] = arr?.filter(
1445
+ (service) =>
1446
+ service.id_retailer === activeRetailer?.id &&
1447
+ service.service === concept
1286
1448
  );
1449
+ return retailerService ? retailerService.status : "NS";
1287
1450
  };
1288
1451
 
1289
- if (state.loading || !state.services || !state.product)
1290
- return (
1291
- <>
1292
- <ProductEditionSkeleton />
1293
- </>
1294
- );
1295
-
1296
- return (
1297
- <AiProductEditionProvider
1298
- isCreatorsEdition={true}
1299
- user={user}
1300
- token={token}
1301
- state={state}
1302
- >
1303
- <Container headerTop={headerTop}>
1304
- {showRejectModal && (
1305
- <Modal
1306
- title={
1307
- rejectAll
1308
- ? "Agregar mensaje para rechazar todo"
1309
- : "Agregar mensaje de rechazo"
1310
- }
1311
- show={showRejectModal}
1312
- customComponent={
1313
- <TagAndInput
1314
- inputType={"textarea"}
1315
- inputId={"modal-message-box"}
1316
- index={0}
1317
- color={"white"}
1318
- />
1319
- }
1320
- buttons={[
1321
- <ButtonV2
1322
- key={"btn-Cancelar"}
1323
- type={"white"}
1324
- label={"Cancelar"}
1325
- size={12}
1326
- onClick={() => {
1327
- setShowRejectModal(false);
1328
- }}
1329
- />,
1330
- <ButtonV2
1331
- key={"btn-Aceptar"}
1332
- type={"pink"}
1333
- label={"Aceptar"}
1334
- size={12}
1335
- onClick={async () => {
1336
- const elements = document.querySelectorAll(
1337
- "#modal-message-box .ql-container .ql-editor > p",
1338
- );
1452
+ useEffect(() => {
1453
+ let status = getRetailerStatus(servicesData, activeTab);
1454
+ setRetailerStatus(status);
1455
+ }, [activeTab, servicesData, activeRetailer]);
1339
1456
 
1340
- if (!elements || elements.length === 0) {
1341
- console.error("Elemento no encontrado");
1342
- return;
1343
- }
1457
+ useEffect(() => {
1458
+ services.length > 0 && getRequired(services);
1459
+ }, [services, servicesData, activeTab]);
1344
1460
 
1345
- const isMessageEmpty = Array.from(elements).every((el) => {
1346
- const body = el.innerHTML;
1347
- return (
1348
- !body || body.replace(/<.*?\/?>/gm, "").trim() === ""
1349
- );
1350
- });
1461
+ useEffect(() => {
1462
+ setSaving(loading);
1463
+ }, [loading]);
1351
1464
 
1352
- if (isMessageEmpty) {
1353
- const container = document.querySelector(
1354
- ".container-customComponent",
1355
- );
1356
- const existingAlert =
1357
- container.querySelector(".alert-error");
1358
-
1359
- if (!existingAlert) {
1360
- const alert = document.createElement("div");
1361
- alert.className = "alert-error";
1362
- alert.style.cssText = `
1363
- color: #d32f2f;
1364
- background-color: #ffebee;
1365
- border: 1px solid #ef5350;
1366
- border-radius: 4px;
1367
- padding: 12px 16px;
1368
- margin-top: 10px;
1369
- font-size: 14px;
1370
- display: flex;
1371
- align-items: center;
1372
- gap: 8px;
1373
- font-family: 'Roboto', sans-serif;
1374
- text-align: center;
1375
- `;
1376
- alert.innerHTML = `<span>El mensaje no puede estar vacío.</span>`;
1377
- container.appendChild(alert);
1378
- }
1379
- return;
1380
- }
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
+ }
1381
1482
 
1382
- const fullMessage = Array.from(elements)
1383
- .map((element) => element.innerHTML)
1384
- .join("")
1385
- .replace(/<br\s*\/?>/gi, "\n");
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);
1386
1505
 
1387
- await createComment(fullMessage, token);
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);
1523
+ }
1524
+ };
1388
1525
 
1389
- rejectAll
1390
- ? sendBulkEvaluation("R")
1391
- : sendSingleEvaluation("R");
1392
- setShowRejectModal(false);
1393
- }}
1394
- />,
1395
- ]}
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
+ }
1534
+ );
1535
+ const parseData = JSON.parse(response.data.body).data[0];
1536
+ setObservation(parseData.observations);
1537
+ };
1538
+ useEffect(() => {
1539
+ getObservation();
1540
+ }, []);
1541
+ 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}
1396
1561
  />
1397
- )}
1398
- {state.modal.show && (
1399
- <GenericModal
1400
- componentsArray={[
1401
- state.modal.image && (
1402
- <img key="1" src={state.modal.image} alt="modal icon" />
1403
- ),
1404
- state.modal.title && (
1405
- <ScreenHeader
1406
- key="2"
1407
- headerType={"retailer-name-header"}
1408
- text={state.modal.title}
1409
- color={"white"}
1410
- />
1411
- ),
1412
- state.modal.message && (
1413
- <ScreenHeader
1414
- key="3"
1415
- headerType={"retailer-name-header"}
1416
- text={state.modal.message}
1417
- color={"white"}
1418
- />
1419
- ),
1420
- state.modal.buttons && (
1421
- <div key="4" style={{ marginTop: "16px" }}>
1422
- {state.modal.buttons.map((button, index) => (
1423
- <Button
1424
- key={index}
1425
- buttonType={button.buttonType}
1426
- label={button.text}
1427
- onClick={button.action}
1428
- />
1429
- ))}
1430
- </div>
1431
- ),
1432
- ].filter(Boolean)}
1433
- onClick={() =>
1434
- dispatch({
1435
- type: "SET_MODAL",
1436
- payload: { show: false, title: "", message: "", image: null },
1437
- })
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()
1438
1573
  }
1574
+ showSaveButton={auditorAssigned() || userAssigned()}
1575
+ setShowVersionSelector={setShowVersionSelector}
1576
+ version={version}
1577
+ shotThd={shotThd}
1439
1578
  />
1440
- )}
1441
- {showModal && (
1442
- <ProductImageModal
1443
- images={state.images_values}
1444
- setShowModal={setShowModal}
1445
- approveRejectButtons={canShowSingleEvaluationButtons()}
1446
- sendToFacilitator={handleOnSendEvaluationToFacilitator}
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())
1597
+ }
1598
+ showValidationButtons={
1599
+ approveRejectButtons() && (auditorAssigned() || userAssigned())
1600
+ }
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}
1447
1610
  />
1448
- )}
1449
- {showVersionSelector && (
1450
- <VersionSelector
1451
- modalId={"version-selector"}
1452
- articleId={state.product.id_article}
1453
- setVersion={handleOnChangeProductVersion}
1454
- companyName={state.product.company}
1455
- currentVersion={state.product.version}
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}
1456
1638
  setShowVersionSelector={setShowVersionSelector}
1457
- jwt={token}
1458
- />
1459
- )}
1460
- <HeaderTop
1461
- setHeaderTop={setHeaderTop}
1462
- auditableVersion={auditableVersion}
1463
- setCompare={setCompare}
1464
- isAuditor={[1, 6].includes(user.id_role)}
1465
- withChat={location?.state?.withChat}
1466
- chatType={location?.state?.chatType}
1467
- productSelected={state.product}
1468
- token={token}
1469
- activeRetailer={{
1470
- id: state.active_retailer?.id_retailer,
1471
- name: state.active_retailer?.retailer,
1472
- }}
1473
- />
1474
- <div className="data-container">
1475
- <div className="image-data-panel">
1476
- <ImagePreviewer
1477
- activeImage={
1478
- state.images_values.values[state.current_image] ?? {}
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;
1479
1657
  }
1480
- imagesArray={state.images_values || []}
1481
- setActiveImage={handleOnChangeCurrentImage}
1482
- setShowModal={handleOnShowModalGallery}
1483
- />
1484
- <ImageDataTable
1485
- // states
1486
- lists={state.images_values || []}
1487
- activeImage={
1488
- state.images_values?.values
1489
- ? state.images_values?.values[state.current_image]
1490
- : {}
1491
- }
1492
- assignationsImages={state.collaborator_assignations["Imágenes"]}
1493
- imagesStatus={state.product?.images_status || "-"}
1494
- retailerSelected={state.active_retailer?.id_retailer}
1495
- isRetailer={isRetailer}
1496
- version={state.product.version}
1497
- shotThd={shotThd}
1498
- setShowVersionSelector={setShowVersionSelector}
1499
- // actions
1500
- setImages={handleOnChangeCurrentImage}
1501
- setAssignation={handleOnChangeAssignations} // No se usa?
1502
- onClickSave={handleOnClickSaveImages}
1503
- showSaveButton={isAuditorAssigned() || isUserAssignedToService()}
1504
- />
1505
- </div>
1658
+ }}
1659
+ canAssign={![7, 8].includes(user.id_role)}
1660
+ />
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
+ )}
1506
1681
 
1507
- <div className="product-information">
1508
- <FullProductNameHeader
1509
- // states
1510
- headerData={state.product}
1511
- productObservation={observation}
1512
- percent={state.active_percentage.percentagesGeneral.required}
1513
- activeRetailer={{
1514
- id: state.active_retailer?.id_retailer,
1515
- name: state.active_retailer?.retailer,
1516
- image: state.active_retailer?.image,
1517
- }}
1518
- servicesData={state.services_data ? state.services_data : null}
1519
- isObservationVisible={isObservationVisible}
1520
- // actions
1521
- setActiveRetailer={handleOnChangeActiveRetailer}
1522
- approve={handleOnApproveSingleService}
1523
- approveAll={handleOnApproveAllServices}
1524
- reject={handleOnRejectSingleService}
1525
- rejectAll={handleOnRejectAllServices}
1526
- toggleObservation={handleOnToggleObservation}
1527
- hideObservation={handleOnHideObservation}
1528
- // sendToFacilitator={handleOnSendEvaluationToFacilitator} // No se usa?
1529
- // validations
1530
- showApproveRejectAll={
1531
- canShowBulkEvaluationButtons() ||
1532
- isAuditorAssigned() ||
1533
- isUserAssignedToService()
1534
- }
1535
- showValidationButtons={
1536
- canShowSingleEvaluationButtons() &&
1537
- (isAuditorAssigned() || isUserAssignedToService())
1538
- }
1539
- />
1540
- <FullTabsMenu
1541
- tabsSections={tabsSections}
1542
- status={getStatusByCurrentServiceAndRetailer()}
1543
- activeTab={state.active_tab}
1544
- isRetailer={isRetailer}
1545
- assig={state.collaborator_assignations[state.active_tab]}
1546
- version={state.product.version}
1547
- updatedDescriptions={state.updated_descriptions_inputs}
1548
- updatedDatasheets={state.updated_datasheets_inputs}
1549
- updatedImages={state.updated_images_values}
1550
- images={state.services.images}
1551
- // selectedImages={selectedImages} prop pasada asi: FullTabsMenu -> TabsMenu Pero en TabsMenu no se espera ninguna prop setSelectedImages
1552
- // setters
1553
- setActiveTab={handleOnChangeActiveTab}
1554
- setImageLayout={handleOnToggleImageLayout}
1555
- setUpdatedDescriptions={handleOnSetUpdatedDescriptions}
1556
- setUpdatedDatasheets={handleOnSetUpdatedDatasheets}
1557
- setAssignation={handleOnSetAssignation} // No se usa?
1558
- setImages={handleOnSetImages} // No se usa?
1559
- setShowVersionSelector={setShowVersionSelector}
1560
- // setSelectedImages={setSelectedImages} prop pasada asi: FullTabsMenu -> TabsMenu Pero en TabsMenu no se espera ninguna prop setSelectedImages
1561
- // actions
1562
- downloadImages={handleOnDownloadImages}
1563
- askToDeleteImages={handleOnAskToDeleteImages}
1564
- showSaveButton={isAuditorAssigned() || isUserAssignedToService()}
1565
- onClickSave={handleOnClickSave}
1566
- // validations
1567
- canAssign={![7, 8].includes(user.id_role)}
1568
- />
1569
- <div
1570
- className={
1571
- "services-information-container " +
1572
- (imageLayout && state.active_tab === "Imágenes"
1573
- ? "image-services"
1574
- : "")
1575
- }
1576
- id="services-information-container"
1577
- >
1578
- {state.saving ? (
1579
- <Loading />
1580
- ) : (
1581
- <>
1582
- {state.active_tab === "Descripción" &&
1583
- (state.product?.description_status !== "NS" ? (
1682
+ {activeTab === "Ficha técnica" &&
1683
+ (product?.datasheet_status !== "NS" ? (
1684
+ datasheets[0]?.data?.map((dataGroup, index) => (
1584
1685
  <InputGroup
1585
- activeSection={state.active_tab}
1586
- inputGroup={state.descriptions_inputs[0]}
1587
- updatedDescriptions={state.updated_descriptions_inputs}
1588
- articleId={state.product?.id_article}
1589
- version={state.product.version}
1590
- auditInputGroup={auditDescriptions[0]}
1591
- setUpdatedDescriptions={handleOnSetUpdatedDescriptions}
1592
- dinamicHeight={true}
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}
1593
1695
  compare={compare}
1594
1696
  />
1595
- ) : (
1596
- <ScreenHeader
1597
- text={"No cuentas con este servicio"}
1598
- headerType={"input-name-header"}
1599
- />
1600
- ))}
1601
- {state.active_tab === "Ficha técnica" &&
1602
- (state.product?.datasheet_status !== "NS" ? (
1603
- <>
1604
- {state.datasheets_inputs[0]?.data?.map(
1605
- (dataGroup, index) => (
1606
- <InputGroup
1607
- key={index + "-" + state.active_retailer.retailer}
1608
- articleId={state.product.id_article}
1609
- version={state.version}
1610
- activeSection={state.active_tab}
1611
- inputGroup={dataGroup}
1612
- dataInputs={state.datasheets_inputs[1]}
1613
- auditInputs={auditDatasheets[1]}
1614
- updatedDatasheets={
1615
- state.updated_datasheets_inputs
1616
- }
1617
- setUpdatedDatasheets={
1618
- handleOnSetUpdatedDatasheets
1619
- }
1620
- compare={compare}
1621
- />
1622
- ),
1623
- )}
1624
- </>
1625
- ) : (
1626
- <ScreenHeader
1627
- text={"No cuentas con este servicio"}
1628
- headerType={"input-name-header"}
1629
- />
1630
- ))}
1631
- {state.active_tab === "Imágenes" &&
1632
- (state.product?.images_status !== "NS" ? (
1633
- <>
1634
- {!imageLayout && (
1635
- <GalleryHeader
1636
- checkAll={state.all_image_checked} // Determina si el checkbox "Seleccionar todo" está marcado
1637
- setCheckAll={handleOnSetCheckAll} // Toggler de true o false para el checkbox "Seleccionar todo"
1638
- setSelectedImages={handleOnSetSelectedImages}
1639
- // shotThd={shotThd} // No se usa?
1640
- />
1641
- )}
1642
- <section
1643
- className="container"
1644
- style={{ position: "relative" }}
1645
- >
1646
- <div
1647
- {...getRootProps({
1648
- className: `dropzone ${
1649
- isDragActive ? "drag-active" : ""
1650
- }`,
1651
- })}
1652
- >
1653
- <input {...getInputProps()} />
1654
- {isDragActive && (
1655
- <div className="drag-overlay">
1656
- <p>Suelta las imágenes aquí</p>
1657
- </div>
1658
- )}
1659
- <aside>{thumbs()}</aside>
1660
- </div>
1661
- {state.images_values?.values.length === 0 && (
1662
- <div
1663
- style={{
1664
- position: "absolute",
1665
- top: "50%",
1666
- left: "50%",
1667
- transform: "translate(-50%, -50%)",
1668
- textAlign: "center",
1669
- padding: "40px",
1670
- width: "80%",
1671
- maxWidth: "500px",
1672
- fontFamily: "Arial, sans-serif",
1673
- }}
1674
- >
1675
- <p
1676
- style={{
1677
- fontSize: "18px",
1678
- color: "#666",
1679
- marginBottom: "16px",
1680
- }}
1681
- >
1682
- Este producto no tiene imágenes
1683
- </p>
1684
- {isAuditorAssigned() ||
1685
- isUserAssignedToService() ? (
1686
- <p
1687
- style={{
1688
- fontSize: "14px",
1689
- color: "#999",
1690
- marginBottom: "20px",
1691
- }}
1692
- >
1693
- Arrastra las imágenes aquí o{" "}
1694
- <span
1695
- onClick={open}
1696
- style={{
1697
- color: "#007bff",
1698
- cursor: "pointer",
1699
- textDecoration: "underline",
1700
- }}
1701
- >
1702
- haz clic para abrir el explorador de
1703
- archivos
1704
- </span>
1705
- </p>
1706
- ) : null}
1707
- </div>
1708
- )}
1709
- </section>
1710
- </>
1711
- ) : (
1712
- <ScreenHeader
1713
- text={"No cuentas con este servicio"}
1714
- headerType={"input-name-header"}
1715
- />
1716
- ))}
1717
- </>
1718
- )}
1719
- </div>
1720
- {(isUserAssignedToService(state.active_tab) ||
1721
- isAuditorAssigned()) &&
1722
- state.product[`${getConceptByTab(state.active_tab)}_status`] !==
1723
- "NS" && (
1724
- <div className="commentary-box">
1725
- {!state.comment ? (
1726
- <div className="commentary">
1727
- <TagAndInput
1728
- label={"Caja de Comentario"}
1729
- inputType={"textarea"}
1730
- inputCols={80}
1731
- inputRows={4}
1732
- inputId={"commentary-box"}
1733
- index={0}
1734
- />
1735
- <div className="buttons-box">
1736
- <Button
1737
- buttonType={"general-transparent-button"}
1738
- label={"Enviar comentario"}
1739
- onClick={handleOnSubmitComment}
1740
- />
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>
1741
1730
  </div>
1742
- </div>
1731
+ </section>
1743
1732
  ) : (
1744
- <div className="feedback-box">
1745
- <Commentary
1746
- comment={state.comment?.message}
1747
- reviewed={crossComment}
1748
- />
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">
1749
1776
  <Button
1750
- buttonType={"circular-button accept-button"}
1751
- onClick={async () => {
1752
- setCrossComment(true);
1753
- commentRevised();
1754
- }}
1777
+ buttonType={"general-transparent-button"}
1778
+ label={"Enviar comentario"}
1779
+ onClick={handleCommentSubmit}
1755
1780
  />
1756
1781
  </div>
1757
- )}
1758
-
1759
- <div className="action-buttons">
1782
+ </div>
1783
+ ) : (
1784
+ <div className="feedback-box">
1785
+ <Commentary
1786
+ comment={comment?.message}
1787
+ reviewed={crossComment}
1788
+ />
1760
1789
  <Button
1761
- buttonType={"general-pink-button"}
1762
- label={"Cambio de Estatus"}
1763
- onClick={() => setOpenChangeStatusModal(true)}
1764
- id="button-change-status"
1790
+ buttonType={"circular-button accept-button"}
1791
+ onClick={async () => {
1792
+ setCrossComment(true);
1793
+ commentRevised();
1794
+ }}
1765
1795
  />
1766
- {[7, 8].includes(user.id_role) && (
1767
- <Button
1768
- buttonType={
1769
- isEvaluationFinished(
1770
- user.id_role,
1771
- state.active_tab,
1772
- statusArray,
1773
- ) &&
1774
- state.missing_required_fields[state.active_tab] === 0
1775
- ? "general-green-button"
1776
- : "general-button-disabled"
1777
- }
1778
- label={"Enviar evaluación"}
1779
- onClick={handleOnSendEvaluationToFacilitator}
1780
- />
1781
- )}
1782
1796
  </div>
1783
- </div>
1784
- )}
1785
- </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
+ )}
1786
1815
  </div>
1787
- </Container>
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
+ }
1788
1917
 
1789
- <ChangeStatusModal
1790
- state={state}
1791
- open={openChangeStatusModal}
1792
- onClose={() => {
1793
- setOpenChangeStatusModal(false);
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
+ }));
1794
1975
  }}
1795
- reloadData={loadData}
1796
- token={token}
1797
1976
  />
1798
- </AiProductEditionProvider>
1799
- );
1800
- };
1801
-
1802
- export const RetailerProductEdition = (props) => {
1803
- return (
1804
- <ProviderProductEditionProvider>
1805
- <RetailerProductEditionView {...props} />
1806
- </ProviderProductEditionProvider>
1977
+ </Container>
1807
1978
  );
1808
1979
  };