contentoh-components-library 21.5.93 → 21.5.95

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