contentoh-components-library 21.5.99 → 21.6.1

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 (64) hide show
  1. package/dist/components/atoms/GeneralButton/styles.js +1 -1
  2. package/dist/components/atoms/GeneralInput/index.js +249 -54
  3. package/dist/components/atoms/GeneralInput/styles.js +7 -3
  4. package/dist/components/atoms/InputFormatter/index.js +223 -68
  5. package/dist/components/atoms/InputFormatter/styles.js +20 -4
  6. package/dist/components/molecules/StatusAsignationInfo/index.js +11 -1
  7. package/dist/components/molecules/TabsMenu/index.js +13 -1
  8. package/dist/components/molecules/TagAndInput/index.js +364 -24
  9. package/dist/components/molecules/TagAndInput/styles.js +2 -2
  10. package/dist/components/organisms/FullProductNameHeader/index.js +6 -22
  11. package/dist/components/organisms/InputGroup/index.js +22 -18
  12. package/dist/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +150 -337
  13. package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.context.js +15 -15
  14. package/dist/components/pages/ProviderProductEdition/index.js +393 -352
  15. package/dist/components/pages/ProviderProductEdition/utils.js +1 -0
  16. package/dist/components/pages/RetailerProductEdition/RetailerProductEdition.stories.js +125 -211
  17. package/dist/components/pages/RetailerProductEdition/index.js +1743 -2239
  18. package/dist/components/pages/RetailerProductEdition/styles.js +4 -2
  19. package/dist/components/pages/RetailerProductEdition/utils.js +251 -2
  20. package/dist/contexts/AiProductEdition.js +34 -22
  21. package/package.json +4 -2
  22. package/src/ai/utils/compare-strings.js +45 -0
  23. package/src/assets/images/Icons/arrow.png +0 -0
  24. package/src/assets/images/Icons/cancel.png +0 -0
  25. package/src/assets/images/Icons/ia-icon.png +0 -0
  26. package/src/assets/images/Icons/loading.svg +5 -0
  27. package/src/assets/images/Icons/reload.png +0 -0
  28. package/src/components/atoms/GeneralButton/styles.js +4 -0
  29. package/src/components/atoms/GeneralInput/index.js +241 -60
  30. package/src/components/atoms/GeneralInput/styles.js +81 -0
  31. package/src/components/atoms/InputFormatter/index.js +200 -51
  32. package/src/components/atoms/InputFormatter/styles.js +284 -0
  33. package/src/components/atoms/RetailerSelector/RetailerSelector.stories.js +10 -0
  34. package/src/components/atoms/RetailerSelector/index.js +3 -0
  35. package/src/components/atoms/RetailerSelector/styles.js +0 -0
  36. package/src/components/molecules/StatusAsignationInfo/index.js +9 -1
  37. package/src/components/molecules/TabsMenu/index.js +12 -0
  38. package/src/components/molecules/TagAndInput/index.js +294 -21
  39. package/src/components/molecules/TagAndInput/styles.js +59 -17
  40. package/src/components/organisms/ChangeStatusModal/index.jsx +488 -0
  41. package/src/components/organisms/ChangeStatusModal/styles.js +333 -0
  42. package/src/components/organisms/FullProductNameHeader/index.js +4 -28
  43. package/src/components/organisms/FullTabsMenu/index.js +1 -1
  44. package/src/components/organisms/InputGroup/index.js +12 -4
  45. package/src/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +174 -202
  46. package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx +14 -14
  47. package/src/components/pages/ProviderProductEdition/index.js +486 -417
  48. package/src/components/pages/ProviderProductEdition/utils.js +2 -2
  49. package/src/components/pages/RetailerProductEdition/RetailerProductEdition.stories.js +136 -243
  50. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.context.jsx +575 -0
  51. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.reducer.js +62 -0
  52. package/src/components/pages/RetailerProductEdition/context/reducers/active-state.js +344 -0
  53. package/src/components/pages/RetailerProductEdition/context/reducers/inputs.js +155 -0
  54. package/src/components/pages/RetailerProductEdition/context/reducers/product.js +114 -0
  55. package/src/components/pages/RetailerProductEdition/context/reducers/system.js +60 -0
  56. package/src/components/pages/RetailerProductEdition/index.js +1580 -1719
  57. package/src/components/pages/RetailerProductEdition/index_old.js +1979 -0
  58. package/src/components/pages/RetailerProductEdition/stories/Auditor.stories.js +101 -0
  59. package/src/components/pages/RetailerProductEdition/stories/ImageEditor.stories.js +115 -0
  60. package/src/components/pages/RetailerProductEdition/stories/TextEditor.stories.js +174 -0
  61. package/src/components/pages/RetailerProductEdition/styles.js +67 -2
  62. package/src/components/pages/RetailerProductEdition/utils.js +240 -0
  63. package/src/contexts/AiProductEdition.jsx +356 -0
  64. package/src/global-files/statusDictionary.js +103 -0
@@ -1,7 +1,17 @@
1
1
  import ReactQuill from "react-quill";
2
2
  import "react-quill/dist/quill.snow.css";
3
- import { useState } from "react";
4
- import { Container } from "./styles";
3
+ import { useEffect, useState } from "react";
4
+ import { ButtonsContainer, Container, InputContainer, OptionsContainer, BottomContainer } from "./styles";
5
+
6
+ import AiGenerationIcon from "../../../assets/images/Icons/ia-icon.png";
7
+ import ArrowIcon from "../../../assets/images/Icons/arrow.png";
8
+ import LoadingIcon from "../../../assets/images/Icons/loading.svg";
9
+ import CancelIcon from "../../../assets/images/Icons/cancel.png";
10
+ import ReloadIcon from "../../../assets/images/Icons/reload.png";
11
+
12
+ import { useAiProductEdition } from "../../../contexts/AiProductEdition";
13
+
14
+ import { getTextSimilarityPercentage } from "../../../ai/utils/compare-strings";
5
15
 
6
16
  export const InputFormatter = ({
7
17
  mainValue = "",
@@ -12,7 +22,29 @@ export const InputFormatter = ({
12
22
  isRequired,
13
23
  maxChar,
14
24
  disabled,
25
+
26
+ //AI Generation
27
+ hasAiGeneration = false,
28
+ isAiGenerationLoading = false,
29
+ isAiRegenerationLoading=false,
30
+ isAiActive = false,
31
+ isAiAvailable = false,
32
+ aiGenerated = false,
33
+
34
+ setIsAiActive = () => {},
35
+ handlerAiGeneration = () => {},
36
+ handlerRegenerateSuggestions = () => {},
37
+ handleChangeSuggestion = () => {}
15
38
  }) => {
39
+
40
+ const {
41
+ suggestions,
42
+ currentSuggestion
43
+ } = useAiProductEdition();
44
+
45
+ const [aiSuggestionAccepted, setAiSuggestionAccepted] = useState(false);
46
+ const [valueAccepted, setValueAccepted] = useState(mainValue);
47
+
16
48
  const [inputValue, setInputValue] = useState(mainValue);
17
49
  const [selection, setSelection] = useState(false);
18
50
  const [position, setPosition] = useState({ left: 0, top: 0 });
@@ -67,32 +99,49 @@ export const InputFormatter = ({
67
99
  setCharsCounter(h.getLength() - 1);
68
100
  value = valueFormater(value);
69
101
 
70
- if (updatedDescriptions) {
71
- let idInput = inputId;
72
- let dataSave = updatedDescriptions?.slice();
73
- if (dataSave?.length > 0) {
74
- const index = dataSave.findIndex((e) => e.attributeId === idInput);
75
- if (index !== -1) {
76
- if (value !== mainValue) dataSave[index].value = value;
77
- else dataSave.splice(index, 1);
102
+ updateParentDescriptions(value, aiSuggestionAccepted);
103
+ };
104
+
105
+ const updateParentDescriptions = (finalValue, isAiAccepted) => {
106
+ if (!updatedDescriptions) return;
107
+
108
+ let idInput = inputId;
109
+ let dataSave = updatedDescriptions?.slice();
110
+
111
+ const similarity = getTextSimilarityPercentage(valueAccepted, inputValue);
112
+
113
+ const generatedWithAi = (isAiAccepted) || aiGenerated && similarity >= 50;
114
+
115
+ if (dataSave?.length > 0) {
116
+ const index = dataSave.findIndex((e) => e.attributeId === idInput);
117
+ if (index !== -1) {
118
+ if (finalValue !== mainValue) {
119
+ dataSave[index].value = finalValue;
120
+ dataSave[index].aiSuggestionAccepted = generatedWithAi;
78
121
  } else {
79
- dataSave.push({
80
- articleId: articleId,
81
- attributeId: idInput,
82
- value: value,
83
- });
122
+ dataSave.splice(index, 1);
84
123
  }
85
124
  } else {
86
- if (value !== mainValue) {
125
+ if (finalValue !== mainValue) {
87
126
  dataSave.push({
88
127
  articleId: articleId,
89
128
  attributeId: idInput,
90
- value: value,
129
+ value: finalValue,
130
+ aiSuggestionAccepted: generatedWithAi,
91
131
  });
92
132
  }
93
133
  }
94
- setUpdatedDescriptions(dataSave);
134
+ } else {
135
+ if (finalValue !== mainValue) {
136
+ dataSave.push({
137
+ articleId: articleId,
138
+ attributeId: idInput,
139
+ value: finalValue,
140
+ aiSuggestionAccepted: generatedWithAi,
141
+ });
142
+ }
95
143
  }
144
+ setUpdatedDescriptions(dataSave);
96
145
  };
97
146
 
98
147
  const getSelection = (range, a, b) => {
@@ -105,6 +154,41 @@ export const InputFormatter = ({
105
154
  }
106
155
  };
107
156
 
157
+ //AI Generation
158
+ const handleAcceptSuggestion = (suggestionValue) => {
159
+ if (!suggestionValue) return;
160
+
161
+ setInputValue(suggestionValue);
162
+ setValueAccepted(suggestionValue);
163
+ setAiSuggestionAccepted(true);
164
+ setIsAiActive(false);
165
+
166
+ // Formateamos el valor sugerido igual que en el onChange
167
+ const formattedSuggestion = valueFormater(suggestionValue);
168
+
169
+ // Disparamos la actualización enviando "true" directamente
170
+ updateParentDescriptions(formattedSuggestion, true);
171
+ };
172
+
173
+ useEffect(() => {
174
+
175
+ // Comprueba cuando la función de IA se cierra y no se acepto una sugerencia
176
+ // Devuelve el valor inicial
177
+ if(!isAiActive && !aiSuggestionAccepted) return setInputValue(valueAccepted);
178
+
179
+ }, [suggestions, isAiActive]);
180
+
181
+ useEffect(() => {
182
+
183
+ if(!isAiActive) return;
184
+
185
+ // Comprueba si la sugerencia fue aceptada
186
+ setAiSuggestionAccepted(false);
187
+
188
+ }, [isAiActive]);
189
+
190
+ //End Ai Generation
191
+
108
192
  return (
109
193
  <Container
110
194
  isRequired={
@@ -114,39 +198,104 @@ export const InputFormatter = ({
114
198
  selection={selection}
115
199
  position={position}
116
200
  >
117
- <ReactQuill
118
- id={inputId + ""}
119
- ref={(el) => setQuill(el)}
120
- defaultValue={getValue(inputValue)}
121
- readOnly={disabled}
122
- modules={{ toolbar: ["bold"] }}
123
- onKeyPress={(e) => {
124
- if (charsCounter >= maxLength && e.key !== "Backspace") {
125
- e.preventDefault();
126
- }
127
- }}
128
- onKeyDown={(e) => {
129
- let keysArray = [
130
- "Backspace",
131
- "Meta",
132
- "ArrowLeft",
133
- "ArrowRight",
134
- "ArrowUp",
135
- "ArrowDown",
136
- ];
137
- if (
138
- charsCounter >= maxLength &&
139
- keysArray.every((key) => e.key !== key)
140
- ) {
141
- e.preventDefault();
142
- }
143
- }}
144
- onChange={(valueInput, user, range, h) => onChange(valueInput, h)}
145
- onChangeSelection={getSelection}
146
- />
147
- <p className="description-limit">
148
- {charsCounter}/{maxLength}
149
- </p>
201
+ <InputContainer className={hasAiGeneration ? "ai-generation" : ""}>
202
+ {
203
+ <ReactQuill
204
+ id={inputId + ""}
205
+ ref={(el) => setQuill(el)}
206
+ value={isAiActive ? (
207
+ currentSuggestion?.[inputId]?.value
208
+ ) : getValue(inputValue)}
209
+ readOnly={isAiActive || disabled}
210
+ modules={{
211
+ toolbar: false,
212
+ }}
213
+ onKeyPress={(e) => {
214
+ if (charsCounter >= maxLength && e.key !== "Backspace") {
215
+ e.preventDefault();
216
+ }
217
+ }}
218
+ onKeyDown={(e) => {
219
+ let keysArray = [
220
+ "Backspace",
221
+ "Meta",
222
+ "ArrowLeft",
223
+ "ArrowRight",
224
+ "ArrowUp",
225
+ "ArrowDown",
226
+ ];
227
+ if (
228
+ charsCounter >= maxLength &&
229
+ keysArray.every((key) => e.key !== key)
230
+ ) {
231
+ e.preventDefault();
232
+ }
233
+ }}
234
+ onChange={(valueInput, user, range, h) => onChange(valueInput, h)}
235
+ onChangeSelection={getSelection}
236
+ className={`quill ${hasAiGeneration && "has-ai"} ${isAiActive && "ai-generation"}`}
237
+ />
238
+ }
239
+ {
240
+ hasAiGeneration && (
241
+ <div className={`icon_container ${isAiActive ? "ai-active" : ''} ${isAiAvailable ? "ai-available" : ''}`} title={!isAiAvailable ? 'Debes de completar ficha técnica e imágenes para desbloquear la generación con IA' : ''} onClick={handlerAiGeneration}>
242
+ <img className={isAiGenerationLoading ? "loading" : ""} src={isAiGenerationLoading ? LoadingIcon : isAiActive ? CancelIcon : AiGenerationIcon} />
243
+ </div>
244
+ )
245
+ }
246
+ </InputContainer>
247
+ <BottomContainer className={isAiActive ? "with-ai" : ""}>
248
+ {
249
+ isAiActive && (
250
+ <div className="ai-options">
251
+
252
+ <OptionsContainer>
253
+ <div className={
254
+ `arrow ${currentSuggestion?.[inputId]?.index === 0 && "disabled"}`
255
+ } onClick={() => {
256
+ handleChangeSuggestion({ action: "prev" })
257
+ }}>
258
+ <img src={ArrowIcon} alt="" />
259
+ </div>
260
+ <p>
261
+ {(currentSuggestion?.[inputId]?.index + 1) || 1}
262
+ /
263
+ {suggestions?.[inputId]?.length}
264
+ </p>
265
+ <div className={`arrow right ${currentSuggestion?.[inputId]?.index === suggestions?.[inputId]?.length - 1 && "disabled"}`} onClick={() => {
266
+
267
+ }}>
268
+ <img onClick={() => {
269
+ handleChangeSuggestion({ action: "next" })
270
+ }} src={ArrowIcon} alt="ai icon" />
271
+ </div>
272
+ </OptionsContainer>
273
+
274
+ <ButtonsContainer>
275
+
276
+ <div className={`reload-suggestions ${isAiRegenerationLoading && "loading"}`} onClick={() => {
277
+ handlerRegenerateSuggestions({
278
+ type: "description"
279
+ });
280
+ }}>
281
+ <img className="" src={isAiRegenerationLoading ? LoadingIcon : ReloadIcon} />
282
+ </div>
283
+
284
+ <div className="accept-suggestion" onClick={() => {
285
+ handleAcceptSuggestion(currentSuggestion?.[inputId]?.value);
286
+ }}>
287
+ <p>Aceptar sugerencia</p>
288
+ </div>
289
+
290
+ </ButtonsContainer>
291
+
292
+ </div>
293
+ )
294
+ }
295
+ <p className="description-limit">
296
+ {charsCounter}/{maxLength}
297
+ </p>
298
+ </BottomContainer>
150
299
  </Container>
151
300
  );
152
301
  };
@@ -10,6 +10,24 @@ export const Container = styled.div`
10
10
  position: relative;
11
11
  border-radius: 5px;
12
12
 
13
+ &.has-ai {
14
+ .ql-container {
15
+ padding-right: 2.5rem;
16
+ }
17
+ }
18
+
19
+ &.ai-generation {
20
+
21
+ .ql-container {
22
+ border: 1px solid #3F8AED;
23
+ border-radius: 5px;
24
+ background-color: rgba(59, 147, 224, 0.05);
25
+ margin: 5px;
26
+ margin-right: 2.5rem;
27
+ }
28
+
29
+ }
30
+
13
31
  .ql-toolbar {
14
32
  background-color: ${GlobalColors.s2};
15
33
  position: absolute;
@@ -44,3 +62,269 @@ export const Container = styled.div`
44
62
  margin-top: 5px;
45
63
  }
46
64
  `;
65
+ export const InputContainer = styled.div`
66
+
67
+ &.ai-generation {
68
+
69
+ position: relative;
70
+
71
+ > input[type=text] {
72
+ padding-right: 2.5rem;
73
+ }
74
+
75
+ > input[type=text].ia-input {
76
+ background-color: rgba(59, 147, 224, 0.05);
77
+ border: 1px solid #3F8AED;
78
+ }
79
+
80
+ .icon_container {
81
+ position: absolute;
82
+ right: 10px;
83
+ top: 7px;
84
+ width: 22.5px;
85
+ height: 22.5px;
86
+ padding: 4px;
87
+
88
+ cursor: not-allowed;
89
+
90
+ border-radius: 50%;
91
+ overflow: hidden;
92
+ display: flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ z-index: 1;
96
+
97
+ &::before {
98
+ content: '';
99
+ position: absolute;
100
+ top: 0;
101
+ left: 0;
102
+ width: 100%;
103
+ height: 100%;
104
+
105
+ background: gray;
106
+ background-size: 200% 200%;
107
+
108
+ animation: ai-shimmer 3s ease-in-out infinite alternate;
109
+ z-index: -1;
110
+ }
111
+
112
+ &.ai-available {
113
+ cursor: pointer;
114
+ }
115
+
116
+ &.ai-available::before{
117
+ background: linear-gradient(
118
+ 120deg,
119
+ #4285F4 20%,
120
+ #33A3C8 50%,
121
+ #4285F4 80%
122
+ );
123
+ background-size: 200% 200%;
124
+ }
125
+
126
+ &.ai-available::after {
127
+ content: "";
128
+ position: absolute;
129
+ top: 0;
130
+ left: 0;
131
+ width: 100%;
132
+ height: 100%;
133
+ opacity: 0.75;
134
+
135
+ background: linear-gradient(
136
+ 135deg,
137
+ rgba(255, 255, 255, 0) 0%,
138
+ rgba(255, 255, 255, 0) 40%,
139
+ rgba(255, 255, 255, 0.8) 50%,
140
+ rgba(255, 255, 255, 0) 60%,
141
+ rgba(255, 255, 255, 0) 100%
142
+ );
143
+
144
+ transform: scale(2) translate(-100%, -100%);
145
+ z-index: 3;
146
+
147
+ animation: ai-glint 3s infinite ease-in-out;
148
+ }
149
+
150
+ &.ai-available img.loading ~ ::after {
151
+ animation: none;
152
+ display: none;
153
+ }
154
+
155
+ &.ai-active::after {
156
+ animation: none;
157
+ display: none;
158
+ }
159
+
160
+ img {
161
+ width: 100%;
162
+ height: 100%;
163
+ object-fit: contain;
164
+ position: relative;
165
+ z-index: 2;
166
+ filter: invert(1) grayscale(1) brightness(2);
167
+ }
168
+
169
+ img.loading {
170
+ width: 22.5px;
171
+ height: 22.5px;
172
+ }
173
+
174
+ &.ai-active::before {
175
+ background: oklch(57.7% 0.245 27.325);
176
+ }
177
+
178
+
179
+ }
180
+ }
181
+
182
+ @keyframes ai-shimmer {
183
+ 0% {
184
+ background-position: 0% 50%;
185
+ }
186
+ 100% {
187
+ background-position: 100% 50%;
188
+ }
189
+ }
190
+
191
+ @keyframes ai-glint {
192
+ 0% {
193
+ transform: scale(2) translate(-100%, -100%);
194
+ }
195
+ 70%, 100% {
196
+ transform: scale(2) translate(100%, 100%);
197
+ }
198
+ }
199
+
200
+ `;
201
+ export const BottomContainer = styled.div`
202
+
203
+ display: flex;
204
+ align-items: center;
205
+ padding-top: 5px;
206
+ justify-content: flex-end;
207
+
208
+ &.with-ai {
209
+ justify-content: space-between;
210
+ }
211
+
212
+ > div.ai-options {
213
+ display: flex;
214
+ align-items: center;
215
+ gap: 0.25rem;
216
+ }
217
+
218
+ `;
219
+
220
+ export const OptionsContainer = styled.div`
221
+
222
+ display: flex;
223
+ align-items: center;
224
+ gap: 0.25rem;
225
+ user-select: none;
226
+
227
+ p {
228
+ font-size: 12px;
229
+ font-family: ${FontFamily.AvenirNext};
230
+ color: ${GlobalColors.deep_gray};
231
+ padding-top: 2px;
232
+ }
233
+
234
+ .arrow {
235
+ width: 20px;
236
+ height: 20px;
237
+ cursor: pointer;
238
+ padding: 2px;
239
+
240
+ border-radius: 5px;
241
+ overflow: hidden;
242
+ display: flex;
243
+ align-items: center;
244
+ justify-content: center;
245
+
246
+ &.disabled {
247
+ opacity: 0.5;
248
+ filter: grayscale(1);
249
+ cursor: not-allowed;
250
+ }
251
+
252
+ :hover {
253
+ background-color: rgba(59, 147, 224, 0.1);
254
+ }
255
+
256
+ &.right {
257
+ transform: rotate(180deg);
258
+ }
259
+
260
+ img {
261
+ width: 100%;
262
+ height: 100%;
263
+ object-fit: contain;
264
+ }
265
+
266
+ }
267
+
268
+ `;
269
+
270
+ export const ButtonsContainer = styled.div`
271
+
272
+ display: flex;
273
+ align-items: center;
274
+ gap: 0.5rem;
275
+
276
+ .reload-suggestions {
277
+
278
+ width: 20px;
279
+ height: 20px;
280
+ display: flex;
281
+ align-items: center;
282
+ justify-content: center;
283
+ border-radius: 5px;
284
+ padding: 4px;
285
+ cursor: pointer;
286
+
287
+ &.loading {
288
+
289
+ filter: grayscale(1);
290
+ opacity: 0.75;
291
+ cursor: not-allowed;
292
+
293
+ > img {
294
+ width: 20px;
295
+ height: 20px;
296
+ }
297
+
298
+ }
299
+
300
+ :hover {
301
+ background-color: rgba(59, 147, 224, 0.1);
302
+ }
303
+
304
+ > img {
305
+ width: 100%;
306
+ height: 100%;
307
+ object-fit: contain;
308
+ }
309
+
310
+ }
311
+
312
+ .accept-suggestion {
313
+
314
+ text-align: center;
315
+ padding: 4px 5px;
316
+ cursor: pointer;
317
+ background-color: rgba(59, 147, 224, 0.05);
318
+
319
+ border-radius: 5px;
320
+
321
+ :hover {
322
+ background-color: rgba(59, 147, 224, 0.1);
323
+ }
324
+
325
+ > p {
326
+ font-size: 11px;
327
+ }
328
+ }
329
+
330
+ `;
@@ -0,0 +1,10 @@
1
+ import { RetailerSelector } from ".";
2
+
3
+ export default {
4
+ component: RetailerSelector,
5
+ parameters: {
6
+ layout: "centered",
7
+ },
8
+ };
9
+
10
+ export const Default = {};
@@ -0,0 +1,3 @@
1
+ export const RetailerSelector = () => {
2
+ return <div>RetailerSelector Component</div>;
3
+ };
@@ -13,6 +13,7 @@ import Slide2 from "../../../assets/images/sliderToolTip/slide22.svg";
13
13
  import Slide3 from "../../../assets/images/sliderToolTip/slide23.svg";
14
14
  import { FinancedCompanies } from "./FinancedCompanies";
15
15
  import { GlobalColors } from "../../../global-files/variables";
16
+ import { useAiProductEdition } from "../../../contexts/AiProductEdition";
16
17
 
17
18
  export const StatusAsignationInfo = ({
18
19
  status = "-",
@@ -37,6 +38,10 @@ export const StatusAsignationInfo = ({
37
38
  const [assignationType, setAssignationType] = useState("facilitator");
38
39
  const isFinanced = FinancedCompanies.includes(user?.id_company);
39
40
 
41
+ const {
42
+ inputsUsingAi
43
+ } = useAiProductEdition();
44
+
40
45
  const closeAsignations = (e) => {
41
46
  if (!e.target.closest("#default-id") && showAsignationPanel) {
42
47
  document.removeEventListener("click", closeAsignations, false);
@@ -93,7 +98,10 @@ export const StatusAsignationInfo = ({
93
98
  {showSaveButton && !isFinanced && (
94
99
  <Button
95
100
  buttonType={"circular-button save-button"}
96
- onClick={onClickSave}
101
+ onClick={() => {
102
+ if(Object.values(inputsUsingAi).some(input => !!input?.using)) return console.warn("Para guardar se debe de dejar de usar la generación con IA");
103
+ onClickSave();
104
+ }}
97
105
  />
98
106
  )}
99
107
  {imagesSection && (
@@ -36,6 +36,18 @@ export const TabsMenu = ({
36
36
  const [llave, setLlave] = useState("");
37
37
  const [objeto, setObjeto] = useState();
38
38
 
39
+ // Sincronizar sections cuando activeTab cambia desde el exterior
40
+ useEffect(() => {
41
+ if (activeTab && Object.keys(tabsSections).length > 0) {
42
+ const tempArray = {};
43
+ Object.keys(tabsSections).forEach((section) => {
44
+ tempArray[section] = section === activeTab;
45
+ });
46
+ setSections(tempArray);
47
+ setImagesSection(activeTab === "Imágenes");
48
+ }
49
+ }, [activeTab, tabsSections]);
50
+
39
51
  const activeSection = (key, array = {}) => {
40
52
  let tempArray = {};
41
53
  setImagesSection(key === "Imágenes");