contentoh-components-library 21.0.62 → 21.0.65

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.
@@ -0,0 +1,1249 @@
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";
10
+ import { saveAs } from "file-saver";
11
+ import {
12
+ getRetailerServices,
13
+ getPercentage,
14
+ fetchUsers,
15
+ } from "../../../global-files/data";
16
+ import { GalleryHeader } from "../../molecules/GalleryHeader";
17
+ import { ProductImageModal } from "../../organisms/ProductImageModal";
18
+ import { useDropzone } from "react-dropzone";
19
+ import axios from "axios";
20
+ import { v4 as uuidv4 } from "uuid";
21
+ import AWS from "aws-sdk";
22
+ import attributesSent from "../../../assets/images/modalsSVGs/attributesSent.svg";
23
+ import descriptionSent from "../../../assets/images/modalsSVGs/descriptionSent.svg";
24
+ import imagesSent from "../../../assets/images/modalsSVGs/uploadingImages.svg";
25
+ import { TagAndInput } from "../../molecules/TagAndInput/index";
26
+ import { Button } from "../../atoms/GeneralButton";
27
+ import { Commentary } from "../../atoms/Commentary";
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 { getNewStatus } from "../../../global-files/data";
33
+
34
+ const reducerImages = (state, action) => {
35
+ let { values, attrForImgs } = state;
36
+ switch (action.action) {
37
+ case "init":
38
+ return action.init;
39
+ case "addImg":
40
+ values = [...values, action.img];
41
+ return { ...state, values };
42
+ case "changeImageInfo":
43
+ values[action.index][action.attribute] = action.value;
44
+ return { ...state, values };
45
+ case "changeAttrValue":
46
+ attrForImgs[action.retailer][action.index].value = action.value;
47
+ return { ...state, attrForImgs, values };
48
+ default:
49
+ return state;
50
+ }
51
+ };
52
+
53
+ const S3_BUCKET = process.env.REACT_APP_IMAGES_BUCKET;
54
+ const REGION = "us-east-1";
55
+
56
+ AWS.config.update({
57
+ accessKeyId: process.env.REACT_APP_KEY_UPLOAD_TO_S3,
58
+ secretAccessKey: process.env.REACT_APP_ACCESS_KEY_UPLOAD_TO_S3,
59
+ });
60
+
61
+ const myBucket = new AWS.S3({
62
+ params: { Bucket: S3_BUCKET },
63
+ region: REGION,
64
+ });
65
+
66
+ export const ProviderProductEdition = ({
67
+ tabsSections,
68
+ productSelected = {},
69
+ user = {},
70
+ location = {},
71
+ assignations = [],
72
+ token,
73
+ }) => {
74
+ const [activeTab, setActiveTab] = useState("Descripción");
75
+ const [activeImage, setActiveImage] = useState();
76
+ const [imageLayout, setImageLayout] = useState(false);
77
+ const [headerTop, setHeaderTop] = useState(0);
78
+ const [descriptions, setDescriptions] = useState([]);
79
+ const [datasheets, setDatasheets] = useState([]);
80
+ const [images, setImages] = useReducer(reducerImages, {});
81
+ const [showModal, setShowModal] = useState(false);
82
+ const { getRootProps, getInputProps } = useDropzone({
83
+ accept: "image/*",
84
+ noKeyboard: true,
85
+ multiple: true,
86
+ noClick: true,
87
+ onDrop: (acceptedFiles) => {
88
+ const newImages = [];
89
+ acceptedFiles.map((file) => {
90
+ const reader = new FileReader();
91
+ reader.fileName = file.name;
92
+ reader.onload = async function (e) {
93
+ const ext = e.srcElement.fileName.split(".");
94
+ const img = new Image();
95
+ img.src = e.target.result;
96
+ const width = img.width;
97
+ const height = img.height;
98
+ const newImg = {
99
+ action: "addImg",
100
+ img: {
101
+ src: e.target.result,
102
+ name: e.target.fileName,
103
+ ext: ext[ext.length - 1],
104
+ width: width,
105
+ height: width,
106
+ },
107
+ };
108
+ setImages(newImg);
109
+ };
110
+ reader.onerror = function (error) {
111
+ console.log("dropzoneError: ", error);
112
+ };
113
+ reader.readAsDataURL(file);
114
+ return file;
115
+ });
116
+ },
117
+ });
118
+ const [updatedDatasheets, setUpdatedDatasheets] = useState([]);
119
+ const [updatedDescriptions, setUpdatedDescriptions] = useState([]);
120
+ const [imagesUploaded, setImagesUploaded] = useState(false);
121
+ const [dataImages, setDataImages] = useState();
122
+ const [percentages, setPercentages] = useState(
123
+ new Array(product?.retailers?.length).fill({ percentage: 0 })
124
+ );
125
+ const [activePercentage, setActivePercentage] = useState(0);
126
+ const [activeRetailer, setActiveRetailer] = useState({});
127
+ const [services, setServices] = useState([]);
128
+ const [servicesData, setServicesData] = useState([]);
129
+ const [message, setMessage] = useState("");
130
+ const [product, setProduct] = useState(
131
+ JSON.parse(sessionStorage.getItem("productSelected"))
132
+ ? JSON.parse(sessionStorage.getItem("productSelected"))
133
+ : productSelected
134
+ );
135
+ const [icon, setIcon] = useState(null);
136
+ const [version, setVersion] = useState(product?.version);
137
+ const [comments, setComments] = useState({});
138
+ const [comment, setComment] = useState("");
139
+ const [requiredNull, setRequiredNull] = useState({
140
+ "Ficha técnica": 0,
141
+ Descripción: 0,
142
+ Imágenes: 0,
143
+ });
144
+ const [crossComment, setCrossComment] = useState(false);
145
+ const [userGroups, setUserGroups] = useState([]);
146
+ const [assig, setAssig] = useState({});
147
+ const [selectedImages, setSelectedImages] = useState([]);
148
+ const [componentsArray, setComponentsArray] = useState([]);
149
+ const [checkAll, setCheckAll] = useState(false);
150
+ const isRetailer = user?.is_retailer;
151
+ const [loading, setLoading] = useState(true);
152
+ const [retailerStatus, setRetailerStatus] = useState("-");
153
+ const [statusArray, setStatusArray] = useState([]);
154
+
155
+ useEffect(() => {
156
+ checkAll && setSelectedImages(images.values);
157
+ }, [checkAll]);
158
+
159
+ const loadData = async () => {
160
+ const services = await getRetailerServices(
161
+ product?.id_article,
162
+ parseInt(product?.id_category),
163
+ product?.version
164
+ );
165
+ //Converts the data inside the datasheets object to array
166
+ setServices(services);
167
+
168
+ //setActiveRetailer(product?.retailers[0]);
169
+ setImages({
170
+ action: "init",
171
+ init: services[2],
172
+ });
173
+ if (services[2]?.values?.length > 0) setActiveImage(0);
174
+
175
+ setActiveRetailer(
176
+ product.retailers ? product.retailers[0] : product.retailersAvailable[0]
177
+ );
178
+
179
+ getPercentage({ data: [product] }).then((res) => setPercentages(res));
180
+ setLoading(false);
181
+ };
182
+
183
+ const getServices = async () => {
184
+ const servicesResponse = await axios.get(
185
+ `${process.env.REACT_APP_SERVICES_ENDPOINT}?articleId=${
186
+ product?.id_article
187
+ }&orderId=${product?.id_order ?? product.orderId}&end=true`
188
+ );
189
+ const parsedResponse = JSON.parse(servicesResponse?.data?.body).data;
190
+ setServicesData(parsedResponse);
191
+ };
192
+
193
+ const translateConcept = (concept) => {
194
+ let translation = "";
195
+ if (concept === "datasheet") {
196
+ translation = "Ficha técnica";
197
+ } else if (concept === "description") {
198
+ translation = "Descripción";
199
+ } else if (concept === "images") {
200
+ translation = "Imágenes";
201
+ }
202
+ return translation;
203
+ };
204
+
205
+ const getComments = async (tab = "Descripción") => {
206
+ const commentsResponse = await Promise.all([
207
+ axios.get(
208
+ `${process.env.REACT_APP_COMMENTS_ENDPOINT}?articleId=${product?.id_article}&concept=description&orderIdColab=${product?.orderId}&version=${version}`
209
+ ),
210
+ axios.get(
211
+ `${process.env.REACT_APP_COMMENTS_ENDPOINT}?articleId=${product?.id_article}&concept=datasheet&orderIdColab=${product?.orderId}&version=${version}`
212
+ ),
213
+ axios.get(
214
+ `${process.env.REACT_APP_COMMENTS_ENDPOINT}?articleId=${product?.id_article}&concept=images&orderIdColab=${product?.orderId}&version=${version}`
215
+ ),
216
+ ]);
217
+
218
+ let comments = {};
219
+ commentsResponse.forEach(
220
+ (comment) =>
221
+ JSON.parse(comment?.data?.body).data[0] &&
222
+ (comments[
223
+ translateConcept(JSON.parse(comment?.data?.body)?.data[0]?.concept)
224
+ ] = JSON.parse(comment?.data?.body).data[0])
225
+ );
226
+ setComment(comments[tab]);
227
+ setComments(comments);
228
+ };
229
+
230
+ useEffect(async () => {
231
+ loadData();
232
+ // getPercentage({ data: [product] }).then((res) =>
233
+ // setPercentages(res)
234
+ // );
235
+ getServices();
236
+ getComments();
237
+ setUserGroups(await fetchUsers(token));
238
+ let arr = [];
239
+ switch (user.id_role) {
240
+ case 7:
241
+ case 8:
242
+ arr = ["IN_PROGRESS", "RF", "RA"];
243
+ break;
244
+ case 4:
245
+ case 5:
246
+ arr = ["RF", "AF", "AA", "AP", "AC"];
247
+ break;
248
+ case 6:
249
+ arr = ["RP", "RC", "AF"];
250
+ break;
251
+ default:
252
+ arr = [];
253
+ break;
254
+ }
255
+ setStatusArray(arr);
256
+ }, [product]);
257
+
258
+ const loadAssignations = (currentProduct) => {
259
+ setAssig(assignations);
260
+ };
261
+
262
+ useEffect(() => {
263
+ loadAssignations(product);
264
+ }, [userGroups]);
265
+
266
+ useEffect(() => {
267
+ product?.retailersAvailable?.forEach((retailer) => {
268
+ retailer["percentage"] = percentages?.filter(
269
+ (percent) => retailer?.id === percent?.id_retailer
270
+ )[0]?.percentage;
271
+ });
272
+ setActivePercentage(product?.retailersAvailable[0]?.percentage);
273
+ }, [percentages]);
274
+
275
+ useEffect(() => {
276
+ if (services.length > 0) {
277
+ if (services[0][activeRetailer.id]?.data)
278
+ services[0][activeRetailer.id].data = Object.values(
279
+ services[0][activeRetailer.id].data
280
+ );
281
+ setActivePercentage(Math.round(activeRetailer?.percentage, 0));
282
+
283
+ const datagroups = services[0][activeRetailer?.id];
284
+ const inputs = services[0]?.inputs;
285
+ const descriptions = services[1]?.filter(
286
+ (service) => service?.id === activeRetailer?.id
287
+ );
288
+ setDatasheets([datagroups, inputs]);
289
+ setDescriptions(descriptions);
290
+ }
291
+ }, [activeRetailer, services]);
292
+
293
+ const thumbs = () => {
294
+ const imageInputs = images?.inputs?.map((e) => ({
295
+ value: e?.id,
296
+ name: e?.name,
297
+ required: e?.required,
298
+ }));
299
+ const imageType = images?.imageType?.map((e) => ({
300
+ value: e?.id,
301
+ name: e?.name,
302
+ }));
303
+ const imagePackagingType = images?.imagePackagingType?.map((e) => ({
304
+ value: e?.id,
305
+ name: e?.name,
306
+ }));
307
+ return images?.values?.map((image, index) => (
308
+ <GalleryElement
309
+ setCheckAll={setCheckAll}
310
+ key={index}
311
+ image={image}
312
+ gridLayout={imageLayout}
313
+ id={"gallery-element-" + index}
314
+ index={index}
315
+ imageType={imageType}
316
+ imagePackagingType={imagePackagingType}
317
+ imageInputs={imageInputs}
318
+ changeImage={setImages}
319
+ selectedImages={selectedImages}
320
+ setSelectedImages={setSelectedImages}
321
+ />
322
+ ));
323
+ };
324
+
325
+ const saveDescriptions = async () => {
326
+ setLoading(true);
327
+ const productTemp = product;
328
+ const dataObject = {
329
+ articleId: product?.id_article,
330
+ articleData: updatedDescriptions,
331
+ };
332
+ if (product?.orderId) dataObject["orderId"] = product?.orderId;
333
+ try {
334
+ await axios.put(
335
+ `${process.env.REACT_APP_ARTICLE_DATA_ENDPOINT}?description=true&version=${version}`,
336
+ dataObject,
337
+ {
338
+ headers: {
339
+ Authorization: token,
340
+ },
341
+ }
342
+ );
343
+ if (productTemp.status === "ASSIGNED") {
344
+ productTemp.status = "IN_PROGRESS";
345
+ setProduct(productTemp);
346
+ sessionStorage.setItem("productSelected", JSON.stringify(productTemp));
347
+ }
348
+
349
+ setMessage("Descripciones guardadas con éxito");
350
+ loadData();
351
+ } catch (error) {
352
+ console.log(error);
353
+ }
354
+ };
355
+
356
+ const saveDatasheets = async () => {
357
+ setLoading(true);
358
+ const dataObject = {
359
+ articleId: product?.id_article,
360
+ articleData: updatedDatasheets,
361
+ };
362
+ if (product?.orderId) dataObject["orderId"] = product?.orderId;
363
+ try {
364
+ await axios.put(
365
+ `${process.env.REACT_APP_ARTICLE_DATA_ENDPOINT}?datasheet=true&version=${version}`,
366
+ dataObject,
367
+ {
368
+ headers: {
369
+ Authorization: token,
370
+ },
371
+ }
372
+ );
373
+ setMessage("Fichas técnicas guardadas");
374
+ if (productTemp.status === "ASSIGNED") {
375
+ productTemp.status = "IN_PROGRESS";
376
+ setProduct(productTemp);
377
+ sessionStorage.setItem("productSelected", JSON.stringify(productTemp));
378
+ }
379
+ loadData();
380
+ } catch (error) {
381
+ console.log(error);
382
+ }
383
+ };
384
+
385
+ const updateImages = useCallback(async () => {
386
+ const imagesList = images?.values?.slice();
387
+ const imagesListTemp = imagesList?.reduce((acc, image) => {
388
+ acc[image?.image_id] = ++acc[image?.image_id] || 0;
389
+ return acc;
390
+ }, {});
391
+
392
+ const duplicated = imagesList?.filter((image) => {
393
+ return imagesListTemp[image?.image_id];
394
+ });
395
+
396
+ const attrForImgs = Object.values(images?.attrForImgs);
397
+ attrForImgs.pop();
398
+ const data = {
399
+ articleId: product?.id_article,
400
+ attrReqImgs: attrForImgs?.map((e) => ({
401
+ attrId: e[0]?.id,
402
+ value: e[0]?.value,
403
+ })),
404
+ articleData: imagesList?.filter((e) => !e.id),
405
+ updateImages: imagesList?.filter((e) => e.id),
406
+ };
407
+ if (product?.orderId) data["orderId"] = product?.orderId;
408
+ let valid =
409
+ data?.articleData?.length === 0
410
+ ? true
411
+ : data?.articleData?.every((e, i) => {
412
+ if (e?.image_id && e?.packing_type && e?.image_type) {
413
+ return true;
414
+ }
415
+ return false;
416
+ });
417
+ if (valid && data?.updateImages?.length > 0 && duplicated?.length === 0) {
418
+ valid = data?.updateImages?.every((e, i) => {
419
+ if (e?.image_id && e?.packing_type && e?.image_type) {
420
+ return true;
421
+ }
422
+ return false;
423
+ });
424
+ }
425
+ if (valid && duplicated?.length === 0) {
426
+ try {
427
+ data?.articleData?.forEach((e) => {
428
+ e.uuid = uuidv4();
429
+ });
430
+ setDataImages(data);
431
+ if (data?.articleData?.length > 0) {
432
+ setImagesUploaded(false);
433
+ const promiseArray = [];
434
+ data?.articleData?.forEach((e) => {
435
+ const file = Buffer.from(
436
+ e.src.replace(/^data:image\/\w+;base64,/, ""),
437
+ "base64"
438
+ );
439
+ const params = {
440
+ ACL: "public-read",
441
+ Body: file,
442
+ Bucket: S3_BUCKET,
443
+ Key: `id-${data.articleId}/${version}/${e?.image_id}-${e?.uuid}.${e?.ext}`,
444
+ };
445
+ promiseArray.push(myBucket.putObject(params).promise());
446
+ });
447
+ await Promise.all(promiseArray);
448
+ setImagesUploaded(true);
449
+ } else {
450
+ setImagesUploaded(true);
451
+ }
452
+ } catch (err) {
453
+ console.log(err);
454
+ // setMainLoading(false);
455
+ }
456
+ } else {
457
+ // setMainLoading(false);
458
+ setMessage(
459
+ "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."
460
+ );
461
+ }
462
+ // eslint-disable-next-line react-hooks/exhaustive-deps
463
+ }, [images, imagesUploaded]);
464
+
465
+ useEffect(async () => {
466
+ if (imagesUploaded) {
467
+ setLoading(true);
468
+ dataImages.articleData = dataImages?.articleData.map((e) => {
469
+ delete e.src;
470
+ e.imageID = e.image_id;
471
+ e.packingType = e.packing_type;
472
+ e.imageType = e.image_type;
473
+ if (product?.orderId) e["orderId"] = product?.orderId;
474
+ return e;
475
+ });
476
+ try {
477
+ await axios.put(
478
+ `${process.env.REACT_APP_ARTICLE_DATA_ENDPOINT}?image=true&version=${version}`,
479
+ dataImages,
480
+ {
481
+ headers: {
482
+ Authorization: token,
483
+ },
484
+ }
485
+ );
486
+ setMessage("Imágenes guardadas con éxito");
487
+ loadData();
488
+ } catch (error) {
489
+ console.log(error);
490
+ }
491
+ }
492
+ }, [dataImages, imagesUploaded]);
493
+
494
+ const evaluationFinished = (userId, tab, statusArray) => {
495
+ const srv = servicesData.filter((serv) => serv.service === getConcept(tab));
496
+ const [srvActive] = srv.filter(
497
+ (serv) => serv.id_retailer === activeRetailer?.id
498
+ );
499
+ const unvalidated =
500
+ product[`${getConcept(tab)}_status`] === "QF" ||
501
+ product[`${getConcept(tab)}_status`] === "IN_PROGRESS";
502
+
503
+ switch (userId) {
504
+ case 7:
505
+ case 8:
506
+ return (
507
+ (["RA", "RF"].includes(product?.status) &&
508
+ statusArray.includes(srvActive?.status)) ||
509
+ (statusArray.includes(product.status) &&
510
+ srv.filter((serv) => statusArray.includes(serv.status)).length ===
511
+ srv.length)
512
+ );
513
+ case 4:
514
+ case 5:
515
+ return (
516
+ unvalidated &&
517
+ ["IN_PROGRESS", "QF"].includes(product.status) &&
518
+ srv.filter((serv) => statusArray.includes(serv.status)).length ===
519
+ srv.length
520
+ );
521
+ case 6:
522
+ return (
523
+ statusArray.includes(product.status) &&
524
+ servicesData.every((serv) => ["RA", "AA"].includes(serv.status))
525
+ );
526
+ default:
527
+ break;
528
+ }
529
+ };
530
+
531
+ const getConcept = (tab) => {
532
+ switch (tab) {
533
+ case "Descripción":
534
+ return "description";
535
+ case "Ficha técnica":
536
+ return "datasheet";
537
+ case "Imágenes":
538
+ return "images";
539
+ }
540
+ };
541
+
542
+ const approveRejectButtons = (action) => {
543
+ let concept = getConcept(activeTab);
544
+ concept = action ? action : concept;
545
+
546
+ const [retailerStatus] = servicesData.filter(
547
+ (srv) => srv.id_retailer === activeRetailer?.id && srv.service === concept
548
+ );
549
+
550
+ return (
551
+ (retailerStatus?.status === "QF" &&
552
+ (user.id_role === 1 || user.id_role === 4 || user.id_role === 5)) ||
553
+ (retailerStatus?.status === "AF" && //sessionStorage product
554
+ (user.id_role === 1 || user.id_role === 6)) ||
555
+ (retailerStatus?.status === "RP" &&
556
+ (user.id_role === 1 || user.id_role === 6)) ||
557
+ (retailerStatus?.status === "RC" &&
558
+ (user.id_role === 1 || user.id_role === 6))
559
+ );
560
+ };
561
+
562
+ const getSectionIcon = () => {
563
+ switch (activeTab) {
564
+ case "Ficha técnica":
565
+ setIcon(attributesSent);
566
+ break;
567
+ case "Descripción":
568
+ setIcon(descriptionSent);
569
+ break;
570
+ case "Imágenes0,,":
571
+ setIcon(imagesSent);
572
+ break;
573
+ default:
574
+ break;
575
+ }
576
+ };
577
+
578
+ const sendToFacilitator = async (result) => {
579
+ setLoading(true);
580
+ try {
581
+ let concept = getConcept(activeTab);
582
+
583
+ let productTemp = { ...product };
584
+ let evalStatus = product[`${concept}_status`];
585
+
586
+ let data = {};
587
+ if (result) {
588
+ //updateCompaniesList(evalStatus, result, activeRetailer.id, concept);
589
+ data = {
590
+ articleId: product.id_article,
591
+ orderId: product.orderId,
592
+ concept: concept,
593
+ result: result,
594
+ evalStatus: evalStatus,
595
+ retailerId: activeRetailer.id,
596
+ };
597
+
598
+ await axios.put(`${process.env.REACT_APP_EVALUATION_ENDPOINT}`, data, {
599
+ headers: {
600
+ Authorization: token,
601
+ },
602
+ });
603
+ getServices();
604
+ } else {
605
+ const specialistDone =
606
+ evalStatus === "RF" ||
607
+ evalStatus === "RA" ||
608
+ evalStatus === "IN_PROGRESS";
609
+
610
+ if (specialistDone) {
611
+ setMessage(`${activeTab} enviada a facilitador`);
612
+ getSectionIcon();
613
+ } else if (evalStatus === "QF") {
614
+ setMessage("Evaluación enviada");
615
+ getSectionIcon();
616
+ } else if (evalStatus === "AF") {
617
+ setMessage("Evaluación enviada");
618
+ getSectionIcon();
619
+ } else if (evalStatus === "RP") {
620
+ setMessage("Evaluación enviada");
621
+ getSectionIcon();
622
+ } else if (evalStatus === "RC") {
623
+ setMessage("Evaluación enviada");
624
+ getSectionIcon();
625
+ }
626
+ let statusArr = [];
627
+ servicesData.forEach((srv) => {
628
+ srv.service === concept && statusArr.push(srv.status);
629
+ });
630
+
631
+ productTemp[`${concept}_status`] = getNewStatus(statusArr);
632
+
633
+ let newStatus = getNewStatus([
634
+ productTemp.datasheet_status,
635
+ productTemp.description_status,
636
+ productTemp.images_status,
637
+ ]);
638
+
639
+ productTemp.status = newStatus;
640
+
641
+ data = {
642
+ articleId: product.id_article,
643
+ orderId: product.orderId,
644
+ concept: concept,
645
+ evalStatus: evalStatus,
646
+ retailerId: activeRetailer.id,
647
+ };
648
+
649
+ switch (user.id_role) {
650
+ case 7:
651
+ case 8:
652
+ data.especialist = true;
653
+ break;
654
+ case 4:
655
+ case 5:
656
+ data.facilitator = true;
657
+ break;
658
+ default:
659
+ break;
660
+ }
661
+ await axios.put(`${process.env.REACT_APP_SEND_EVAL}`, data, {
662
+ headers: {
663
+ Authorization: token,
664
+ },
665
+ });
666
+ getServices();
667
+ }
668
+ sessionStorage.setItem("productSelected", JSON.stringify(productTemp));
669
+ setProduct(productTemp);
670
+ } catch (error) {
671
+ console.log(error);
672
+ }
673
+ setLoading(false);
674
+ };
675
+
676
+ const userAssigned = (tab, rol) => {
677
+ let concept = "";
678
+ switch (tab) {
679
+ case "Ficha técnica":
680
+ concept = "datasheet";
681
+ break;
682
+ case "Imágenes":
683
+ concept = "images";
684
+ break;
685
+
686
+ default:
687
+ concept = "description";
688
+ break;
689
+ }
690
+
691
+ const allowedRoles = [1, 4, 5, 6, 7, 8];
692
+ const validUser = allowedRoles.indexOf(user?.id_role) !== -1;
693
+
694
+ if (!rol) {
695
+ switch (user.id_role) {
696
+ case 4:
697
+ case 5:
698
+ rol = "facilitator";
699
+ break;
700
+ case 7:
701
+ case 8:
702
+ rol = "especialist";
703
+ break;
704
+ }
705
+ }
706
+
707
+ return true;
708
+ };
709
+
710
+ const auditorAssigned = () => {
711
+ return product?.article[`id_auditor`] === user.id_user;
712
+ };
713
+
714
+ const createComment = async (e, body, tab) => {
715
+ let concept = "";
716
+ switch (activeTab) {
717
+ case "Ficha técnica":
718
+ concept = "datasheet";
719
+ break;
720
+ case "Imágenes":
721
+ concept = "images";
722
+ break;
723
+
724
+ default:
725
+ concept = "description";
726
+ break;
727
+ }
728
+ const data = {
729
+ articleId: product?.id_article,
730
+ orderId: product?.orderId,
731
+ message: body,
732
+ concept: concept,
733
+ version: version,
734
+ };
735
+ await axios.post(`${process.env.REACT_APP_COMMENTS_ENDPOINT}`, data, {
736
+ headers: {
737
+ Authorization: token,
738
+ },
739
+ });
740
+ await getComments(tab);
741
+ };
742
+
743
+ const getRequired = (services) => {
744
+ const objetcTemp = {};
745
+ const datasheetServicesArray = Object.values(services[0]);
746
+ const dsInputs = datasheetServicesArray.pop();
747
+ const descriptionsServicesArray = services[1];
748
+
749
+ let dsInputsRequired = [];
750
+ let desInputsRequired = 0;
751
+ datasheetServicesArray.forEach((datasheet) => {
752
+ const [requested] = servicesData.filter(
753
+ (srv) =>
754
+ srv.id_retailer === datasheet.retailer.id &&
755
+ srv.service === getConcept(activeTab)
756
+ );
757
+ requested &&
758
+ Object.values(datasheet?.data).forEach((dataGroup) =>
759
+ dsInputsRequired.push(
760
+ ...dataGroup.inputs.filter(
761
+ (input) =>
762
+ dsInputs[input].required &&
763
+ (dsInputs[input].value === undefined || !dsInputs[input].value)
764
+ )
765
+ )
766
+ );
767
+ });
768
+
769
+ objetcTemp["Ficha técnica"] = dsInputsRequired.length;
770
+
771
+ descriptionsServicesArray.forEach((description) => {
772
+ const [requested] = servicesData.filter(
773
+ (srv) =>
774
+ srv.id_retailer === description.id &&
775
+ srv.service === getConcept(activeTab)
776
+ );
777
+ requested &&
778
+ description.inputs.forEach(
779
+ (input) =>
780
+ input.required &&
781
+ (!input.value ||
782
+ input.value.replace(/(<\/?p>)|(<\/?strong>)|(<br>)/gm, "") ===
783
+ "") &&
784
+ desInputsRequired++
785
+ );
786
+ });
787
+
788
+ objetcTemp["Descripción"] = desInputsRequired;
789
+ const requiredImages = services[2]?.inputs?.filter((e) => e.required);
790
+ let requiredCounter = 0;
791
+ requiredImages?.forEach(
792
+ (req) =>
793
+ services[2].values.filter((img) => img.image_id === req.id).length ===
794
+ 0 && requiredCounter++
795
+ );
796
+ objetcTemp["Imágenes"] = requiredCounter;
797
+ setRequiredNull(objetcTemp);
798
+ };
799
+
800
+ useEffect(() => {
801
+ setComment(comments[activeTab]);
802
+ }, [activeTab]);
803
+
804
+ const commentRevised = async () => {
805
+ const data = {
806
+ commentId: comment.id,
807
+ };
808
+ await axios.put(`${process.env.REACT_APP_COMMENTS_ENDPOINT}`, data, {
809
+ headers: {
810
+ Authorization: sessionStorage.getItem("jwt"),
811
+ },
812
+ });
813
+ setCrossComment(false);
814
+ await getComments();
815
+ };
816
+
817
+ const setAssignation = async (assignationType, assignationId) => {
818
+ let concept = "";
819
+ switch (activeTab) {
820
+ case "Ficha técnica":
821
+ concept = "datasheet";
822
+ break;
823
+ case "Imágenes":
824
+ concept = "images";
825
+ break;
826
+
827
+ default:
828
+ concept = "description";
829
+ break;
830
+ }
831
+ const productTemp = product;
832
+ productTemp.article[`id_${concept}_${assignationType}`] = assignationId;
833
+ const data = {
834
+ articleList: [
835
+ {
836
+ orderId: product.orderId,
837
+ articleId: product?.id_article,
838
+ },
839
+ ],
840
+ concept: concept,
841
+ [`${assignationType}Id`]: assignationId,
842
+ };
843
+ axios({
844
+ method: "post",
845
+ url: process.env.REACT_APP_ASSIGNATIONS_ENDPOINT,
846
+ data: data,
847
+ headers: {
848
+ Authorization: token,
849
+ },
850
+ });
851
+ loadAssignations(productTemp);
852
+ sessionStorage.setItem("productSelected", JSON.stringify(productTemp));
853
+ };
854
+
855
+ const evaluationComplete = () => {
856
+ let concept = "";
857
+ switch (activeTab) {
858
+ case "Ficha técnica":
859
+ concept = "datasheet";
860
+ break;
861
+ case "Imágenes":
862
+ concept = "images";
863
+ break;
864
+ case "Descripción":
865
+ concept = "description";
866
+ break;
867
+ }
868
+
869
+ let serv = servicesData.filter((item) => item.service === concept);
870
+ let approved = "";
871
+ let rejected = "";
872
+ if (
873
+ product.status === "IN_PROGRESS" ||
874
+ product.status === "QF" ||
875
+ product.status === "RF"
876
+ ) {
877
+ approved = "AF";
878
+ rejected = "RF";
879
+ if (product.status === "IN_PROGRESS" || product.status === "QF") {
880
+ return (
881
+ serv.filter(
882
+ (item) =>
883
+ item.status === approved ||
884
+ item.status === rejected ||
885
+ item.status === "AA" ||
886
+ item.status === "IN_PROGRESS" ||
887
+ item.status === "AP"
888
+ ).length === serv.length
889
+ );
890
+ }
891
+ } else if (
892
+ product.status === "AF" ||
893
+ product.status === "RA" ||
894
+ product.status === "RP"
895
+ ) {
896
+ approved = "AA";
897
+ rejected = "RA";
898
+
899
+ if (product.status === "RP" || product.status === "AF") {
900
+ return (
901
+ serv.filter(
902
+ (item) =>
903
+ item.status === approved ||
904
+ item.status === rejected ||
905
+ item.status === "AP"
906
+ ).length === serv.length
907
+ );
908
+ }
909
+ } else if (product.status === "RC") {
910
+ approved = "AP";
911
+ rejected = "RA";
912
+ return (
913
+ serv.filter(
914
+ (item) => item.status === approved || item.status === rejected
915
+ ).length === serv.length
916
+ );
917
+ }
918
+ return (
919
+ serv.filter(
920
+ (item) => item.status === approved || item.status === rejected
921
+ ).length === serv.length
922
+ );
923
+ };
924
+
925
+ const downloadImages = () => {
926
+ selectedImages.forEach((e) => {
927
+ if (e.id) {
928
+ saveAs(
929
+ `https://${process.env.REACT_APP_IMAGES_BUCKET}.s3.amazonaws.com/${e.srcDB}`,
930
+ `${product.article.upc}_${e.name}.${e.ext}`
931
+ );
932
+ }
933
+ });
934
+ };
935
+
936
+ const deleteImages = () => {
937
+ const data = {
938
+ articleId: product.id_article,
939
+ deleteImages: selectedImages,
940
+ };
941
+ try {
942
+ axios.put(
943
+ `${process.env.REACT_APP_ARTICLE_DATA_ENDPOINT}?image=true&version=${version}`,
944
+ data,
945
+ {
946
+ headers: { Authorization: token },
947
+ }
948
+ );
949
+ loadData();
950
+ } catch (err) {
951
+ console.log(err);
952
+ }
953
+ setMessage("");
954
+ };
955
+
956
+ const askToDeleteImages = () => {
957
+ if (selectedImages.length > 0) {
958
+ setMessage("¿Está seguro de eliminar las imágenes seleccionadas?");
959
+ setComponentsArray([
960
+ <ScreenHeader
961
+ key={"1"}
962
+ text={"¿Está seguro de eliminar las imágenes seleccionadas?"}
963
+ headerType="retailer-name-header"
964
+ color={"white"}
965
+ />,
966
+ <Button
967
+ key={"2"}
968
+ buttonType="general-white-button"
969
+ label={"Cancelar"}
970
+ onClick={() => setMessage("")}
971
+ />,
972
+ <Button
973
+ key={"3"}
974
+ buttonType="general-button-default"
975
+ label={"Aceptar"}
976
+ onClick={() => deleteImages()}
977
+ />,
978
+ ]);
979
+ }
980
+ };
981
+
982
+ const specialistValid = (tab) => {
983
+ let concept = "";
984
+ switch (tab) {
985
+ case "Ficha técnica":
986
+ concept = "datasheet";
987
+ break;
988
+ case "Imágenes":
989
+ concept = "images";
990
+ break;
991
+ case "Descripción":
992
+ concept = "description";
993
+ break;
994
+ }
995
+ return (
996
+ product[`${concept}_status`] === "IN_PROGRESS" ||
997
+ product[`${concept}_status`] === "RF" ||
998
+ product[`${concept}_status`] === "RA"
999
+ );
1000
+ };
1001
+
1002
+ const getRetailerStatus = (servicesData, tab) => {
1003
+ const arr = servicesData?.slice();
1004
+ let concept = getConcept(tab);
1005
+
1006
+ let retailerService = {};
1007
+ [retailerService] = arr?.filter(
1008
+ (service) =>
1009
+ service.id_retailer === activeRetailer?.id &&
1010
+ service.service === concept
1011
+ );
1012
+ return retailerService ? retailerService.status : "NA";
1013
+ };
1014
+
1015
+ useEffect(() => {
1016
+ let status = getRetailerStatus(servicesData, activeTab);
1017
+ setRetailerStatus(status);
1018
+ }, [activeTab, servicesData, activeRetailer]);
1019
+
1020
+ useEffect(() => {
1021
+ services.length > 0 && getRequired(services);
1022
+ }, [services, servicesData, activeTab]);
1023
+
1024
+ return (
1025
+ <Container headerTop={headerTop}>
1026
+ <HeaderTop setHeaderTop={setHeaderTop} />
1027
+ <div className="data-container">
1028
+ <div className="image-data-panel">
1029
+ <ImagePreviewer
1030
+ activeImage={images?.values ? images?.values[activeImage] : {}}
1031
+ imagesArray={images}
1032
+ setActiveImage={setActiveImage}
1033
+ setShowModal={setShowModal}
1034
+ />
1035
+ <ImageDataTable
1036
+ lists={images}
1037
+ activeImage={images?.values ? images?.values[activeImage] : {}}
1038
+ retailerSelected={activeRetailer?.id}
1039
+ setImages={setImages}
1040
+ assignationsImages={assig["Imágenes"]}
1041
+ imagesStatus={product?.images_status}
1042
+ setAssignation={setAssignation}
1043
+ isRetailer={isRetailer}
1044
+ onClickSave={() =>
1045
+ product?.services?.images === 1 && updateImages()
1046
+ }
1047
+ />
1048
+ </div>
1049
+ <div className="product-information">
1050
+ <FullProductNameHeader
1051
+ headerData={product}
1052
+ percent={activePercentage}
1053
+ activeRetailer={activeRetailer}
1054
+ setActiveRetailer={setActiveRetailer}
1055
+ approveRejectButtons={approveRejectButtons}
1056
+ sendToFacilitator={sendToFacilitator}
1057
+ auditorAssigned={auditorAssigned}
1058
+ userAssigned={() => userAssigned(activeTab)}
1059
+ />
1060
+ <FullTabsMenu
1061
+ tabsSections={tabsSections}
1062
+ status={retailerStatus}
1063
+ activeTab={activeTab}
1064
+ setActiveTab={setActiveTab}
1065
+ setImageLayout={setImageLayout}
1066
+ downloadImages={downloadImages}
1067
+ askToDeleteImages={askToDeleteImages}
1068
+ assig={assig[activeTab]}
1069
+ setAssignation={setAssignation}
1070
+ isRetailer={isRetailer}
1071
+ onClickSave={() => {
1072
+ switch (activeTab) {
1073
+ case "Descripción":
1074
+ product?.services?.descriptions === 1 && saveDescriptions();
1075
+ break;
1076
+ case "Ficha técnica":
1077
+ product?.services?.datasheets === 1 && saveDatasheets();
1078
+ break;
1079
+ case "Imágenes":
1080
+ product?.services?.images === 1 && updateImages();
1081
+ break;
1082
+
1083
+ default:
1084
+ break;
1085
+ }
1086
+ }}
1087
+ />
1088
+ <div
1089
+ className={
1090
+ "services-information-container " +
1091
+ (imageLayout && activeTab === "Imágenes" ? "image-services" : "")
1092
+ }
1093
+ >
1094
+ {loading ? (
1095
+ <Loading />
1096
+ ) : (
1097
+ <>
1098
+ {!imageLayout &&
1099
+ activeTab === "Imágenes" &&
1100
+ product?.services?.images === 1 && (
1101
+ <GalleryHeader
1102
+ setSelectedImages={setSelectedImages}
1103
+ checkAll={checkAll}
1104
+ setCheckAll={setCheckAll}
1105
+ />
1106
+ )}
1107
+ {activeTab === "Ficha técnica" &&
1108
+ (product?.datasheet_status !== "NS" ? (
1109
+ datasheets[0]?.data?.map((dataGroup, index) => (
1110
+ <InputGroup
1111
+ key={index + "-" + activeRetailer.name}
1112
+ articleId={product.id_article}
1113
+ version={version}
1114
+ activeSection={activeTab}
1115
+ inputGroup={dataGroup}
1116
+ dataInputs={datasheets[1]}
1117
+ updatedDatasheets={updatedDatasheets}
1118
+ setUpdatedDatasheets={setUpdatedDatasheets}
1119
+ />
1120
+ ))
1121
+ ) : (
1122
+ <ScreenHeader
1123
+ text={"No cuentas con este servicio"}
1124
+ headerType={"input-name-header"}
1125
+ />
1126
+ ))}
1127
+ {activeTab === "Descripción" &&
1128
+ (product?.description_status !== "NS" ? (
1129
+ <InputGroup
1130
+ activeSection={activeTab}
1131
+ inputGroup={descriptions[0]}
1132
+ updatedDescriptions={updatedDescriptions}
1133
+ setUpdatedDescriptions={setUpdatedDescriptions}
1134
+ articleId={product?.id_article}
1135
+ version={version}
1136
+ dinamicHeight={true}
1137
+ />
1138
+ ) : (
1139
+ <ScreenHeader
1140
+ text={"No cuentas con este servicio"}
1141
+ headerType={"input-name-header"}
1142
+ />
1143
+ ))}
1144
+
1145
+ {activeTab === "Imágenes" &&
1146
+ (product?.images_status !== "NS" ? (
1147
+ <section className="container">
1148
+ <div {...getRootProps({ className: "dropzone" })}>
1149
+ <input {...getInputProps()} />
1150
+ <aside>{thumbs()}</aside>
1151
+ </div>
1152
+ </section>
1153
+ ) : (
1154
+ <ScreenHeader
1155
+ text={"No cuentas con este servicio"}
1156
+ headerType={"input-name-header"}
1157
+ />
1158
+ ))}
1159
+ </>
1160
+ )}
1161
+ </div>
1162
+ {(userAssigned(activeTab) || auditorAssigned()) &&
1163
+ product[`${getConcept(activeTab)}_status`] !== "NS" && (
1164
+ <div className="commentary-box">
1165
+ {!comment ? (
1166
+ <div className="commentary">
1167
+ <TagAndInput
1168
+ label={"Caja de Comentario"}
1169
+ inputType={"textarea"}
1170
+ inputCols={80}
1171
+ inputRows={4}
1172
+ inputId={"commentary-box"}
1173
+ index={0}
1174
+ />
1175
+ <div className="buttons-box">
1176
+ <Button
1177
+ buttonType={"general-transparent-button"}
1178
+ label={"Enviar comentario"}
1179
+ onClick={(e) =>
1180
+ createComment(
1181
+ e,
1182
+ document.querySelector(
1183
+ "#description-commentary-box-0 .ql-container .ql-editor > p"
1184
+ ).innerHTML,
1185
+ activeTab
1186
+ )
1187
+ }
1188
+ />
1189
+ </div>
1190
+ </div>
1191
+ ) : (
1192
+ <div className="feedback-box">
1193
+ <Commentary
1194
+ comment={comment.message}
1195
+ reviewed={crossComment}
1196
+ />
1197
+ <Button
1198
+ buttonType={"circular-button accept-button"}
1199
+ onClick={async () => {
1200
+ setCrossComment(true);
1201
+ commentRevised();
1202
+ }}
1203
+ />
1204
+ </div>
1205
+ )}
1206
+ <Button
1207
+ buttonType={
1208
+ evaluationFinished(user.id_role, activeTab, statusArray) &&
1209
+ requiredNull[activeTab] === 0
1210
+ ? "general-green-button"
1211
+ : "general-button-disabled"
1212
+ }
1213
+ label={"Enviar evaluación"}
1214
+ onClick={() => sendToFacilitator()}
1215
+ />
1216
+ </div>
1217
+ )}
1218
+ </div>
1219
+ </div>
1220
+ {showModal && (
1221
+ <ProductImageModal
1222
+ images={images}
1223
+ setShowModal={setShowModal}
1224
+ sendToFacilitator={sendToFacilitator}
1225
+ approveRejectButtons={approveRejectButtons}
1226
+ />
1227
+ )}
1228
+ {message.length > 0 && (
1229
+ <GenericModal
1230
+ buttonType={componentsArray.length > 0 && "delete-product"}
1231
+ componentsArray={
1232
+ componentsArray.length > 0
1233
+ ? componentsArray
1234
+ : [
1235
+ <img key="1" src={succes} alt="success icon" />,
1236
+ <ScreenHeader
1237
+ key="2"
1238
+ headerType={"retailer-name-header"}
1239
+ text={message}
1240
+ color={"white"}
1241
+ />,
1242
+ ]
1243
+ }
1244
+ onClick={() => setMessage("")}
1245
+ />
1246
+ )}
1247
+ </Container>
1248
+ );
1249
+ };