contentoh-components-library 21.5.99 → 21.6.0

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 (63) 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/package.json +4 -2
  21. package/src/ai/utils/compare-strings.js +45 -0
  22. package/src/assets/images/Icons/arrow.png +0 -0
  23. package/src/assets/images/Icons/cancel.png +0 -0
  24. package/src/assets/images/Icons/ia-icon.png +0 -0
  25. package/src/assets/images/Icons/loading.svg +5 -0
  26. package/src/assets/images/Icons/reload.png +0 -0
  27. package/src/components/atoms/GeneralButton/styles.js +4 -0
  28. package/src/components/atoms/GeneralInput/index.js +241 -60
  29. package/src/components/atoms/GeneralInput/styles.js +81 -0
  30. package/src/components/atoms/InputFormatter/index.js +200 -51
  31. package/src/components/atoms/InputFormatter/styles.js +284 -0
  32. package/src/components/atoms/RetailerSelector/RetailerSelector.stories.js +10 -0
  33. package/src/components/atoms/RetailerSelector/index.js +3 -0
  34. package/src/components/atoms/RetailerSelector/styles.js +0 -0
  35. package/src/components/molecules/StatusAsignationInfo/index.js +9 -1
  36. package/src/components/molecules/TabsMenu/index.js +12 -0
  37. package/src/components/molecules/TagAndInput/index.js +294 -21
  38. package/src/components/molecules/TagAndInput/styles.js +59 -17
  39. package/src/components/organisms/ChangeStatusModal/index.jsx +488 -0
  40. package/src/components/organisms/ChangeStatusModal/styles.js +333 -0
  41. package/src/components/organisms/FullProductNameHeader/index.js +4 -28
  42. package/src/components/organisms/FullTabsMenu/index.js +1 -1
  43. package/src/components/organisms/InputGroup/index.js +12 -4
  44. package/src/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +174 -202
  45. package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx +14 -14
  46. package/src/components/pages/ProviderProductEdition/index.js +486 -417
  47. package/src/components/pages/ProviderProductEdition/utils.js +2 -2
  48. package/src/components/pages/RetailerProductEdition/RetailerProductEdition.stories.js +136 -243
  49. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.context.jsx +575 -0
  50. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.reducer.js +62 -0
  51. package/src/components/pages/RetailerProductEdition/context/reducers/active-state.js +344 -0
  52. package/src/components/pages/RetailerProductEdition/context/reducers/inputs.js +155 -0
  53. package/src/components/pages/RetailerProductEdition/context/reducers/product.js +114 -0
  54. package/src/components/pages/RetailerProductEdition/context/reducers/system.js +60 -0
  55. package/src/components/pages/RetailerProductEdition/index.js +1580 -1719
  56. package/src/components/pages/RetailerProductEdition/index_old.js +1979 -0
  57. package/src/components/pages/RetailerProductEdition/stories/Auditor.stories.js +101 -0
  58. package/src/components/pages/RetailerProductEdition/stories/ImageEditor.stories.js +115 -0
  59. package/src/components/pages/RetailerProductEdition/stories/TextEditor.stories.js +174 -0
  60. package/src/components/pages/RetailerProductEdition/styles.js +67 -2
  61. package/src/components/pages/RetailerProductEdition/utils.js +240 -0
  62. package/src/contexts/AiProductEdition.jsx +347 -0
  63. package/src/global-files/statusDictionary.js +103 -0
@@ -3,6 +3,16 @@ import { Container } from "./styles";
3
3
  import { InputFormatter } from "../InputFormatter";
4
4
  import { CheckBox } from "../CheckBox";
5
5
 
6
+ import AiGenerationIcon from "../../../assets/images/Icons/ia-icon.png";
7
+ import LoadingIcon from "../../../assets/images/Icons/loading.svg";
8
+ import ArrowIcon from "../../../assets/images/Icons/arrow.png";
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
+ import { InputContainer, BottomContainer, OptionsContainer, ButtonsContainer } from "../InputFormatter/styles";
14
+ import { getTextSimilarityPercentage } from "../../../ai/utils/compare-strings";
15
+
6
16
  export const GeneralInput = ({
7
17
  inputType,
8
18
  inputId,
@@ -27,45 +37,68 @@ export const GeneralInput = ({
27
37
  disabled,
28
38
  onKeyDown,
29
39
  auditClass,
40
+
41
+ //AI Generation
42
+ hasAiGeneration = false,
43
+ isBenefitInput = false,
44
+ isAiGenerationLoading = false,
45
+ isAiRegenerationLoading=false,
46
+ isAiActive = false,
47
+ isAiAvailable = false,
48
+ aiGenerated = false,
49
+
50
+ setIsAiActive = () => {},
51
+ handlerAiGeneration = () => {},
52
+ handlerRegenerateSuggestions = () => {},
53
+ handleChangeSuggestion = () => {}
54
+
30
55
  }) => {
56
+
57
+ const {
58
+ isCreators,
59
+ suggestions,
60
+ currentSuggestion,
61
+ setCurrentSuggestionValue
62
+ } = useAiProductEdition();
63
+
31
64
  const [textValue, setTextValue] = useState({
32
65
  value: inputValue,
33
66
  });
34
67
  const [requiredEmpty, setRequiredEmpty] = useState(false);
35
68
 
36
- const onHandleChange = (evt) => {
37
- if (validateInput) {
38
- setTextValue({ value: validateInput(evt, position, inputsArray) });
39
- } else if (
40
- updatedDatasheets ||
41
- updatedDescriptions ||
42
- inputType === "textarea"
43
- ) {
44
- let generalValue;
45
- if (optionList?.length > 0) {
46
- let valueSelected = evt.target.value;
47
- generalValue = valueSelected;
48
- setTextValue({ value: generalValue });
49
- } else {
50
- generalValue =
51
- inputType === "checkbox" ? evt.target.checked : evt.target.value;
52
- setTextValue({
53
- value: generalValue,
54
- });
55
- }
56
- let dataSave = updatedDatasheets?.slice();
69
+ const [aiSuggestionAccepted, setAiSuggestionAccepted] = useState(false);
70
+ const [valueAccepted, setValueAccepted] = useState(inputValue);
71
+
72
+ const updateParentData = (generalValue, isAiAccepted, newBaseValue = null) => {
73
+ setTextValue({ value: generalValue });
74
+
75
+ const baseToCompare = newBaseValue !== null ? newBaseValue : valueAccepted;
76
+
77
+ const similarity = getTextSimilarityPercentage(baseToCompare, generalValue);
78
+
79
+ const generatedWithAi = (isAiAccepted || aiGenerated) && similarity >= 50;
80
+
81
+ if (updatedDatasheets || updatedDescriptions || inputType === "textarea") {
82
+ let dataSave = updatedDatasheets?.slice() || [];
57
83
 
58
84
  if (dataSave?.length > 0) {
59
85
  const index = dataSave.findIndex((e) => e.attributeId === inputId);
60
86
  if (index !== -1) {
61
- if (generalValue !== inputValue) dataSave[index].value = generalValue;
62
- else dataSave.splice(index, 1);
87
+ if (generalValue !== inputValue) {
88
+ dataSave[index].value = generalValue;
89
+ dataSave[index].aiSuggestionAccepted = generatedWithAi;
90
+ } else {
91
+ dataSave.splice(index, 1);
92
+ }
63
93
  } else {
64
- dataSave.push({
65
- articleId: articleId,
66
- attributeId: inputId,
67
- value: generalValue,
68
- });
94
+ if (generalValue !== inputValue) {
95
+ dataSave.push({
96
+ articleId: articleId,
97
+ attributeId: inputId,
98
+ value: generalValue,
99
+ aiSuggestionAccepted: generatedWithAi,
100
+ });
101
+ }
69
102
  }
70
103
  } else {
71
104
  if (generalValue !== inputValue) {
@@ -73,16 +106,72 @@ export const GeneralInput = ({
73
106
  articleId: articleId,
74
107
  attributeId: inputId,
75
108
  value: generalValue,
109
+ aiSuggestionAccepted: generatedWithAi,
76
110
  });
77
111
  }
78
112
  }
113
+
79
114
  setUpdatedDatasheets(dataSave);
115
+ }
116
+ };
117
+
118
+ const onHandleChange = (evt) => {
119
+ if (validateInput) {
120
+ setTextValue({ value: validateInput(evt, position, inputsArray) });
121
+ } else if (
122
+ updatedDatasheets ||
123
+ updatedDescriptions ||
124
+ inputType === "textarea"
125
+ ) {
126
+ let generalValue;
127
+ if (optionList?.length > 0) {
128
+ generalValue = evt.target.value;
129
+ } else {
130
+ generalValue =
131
+ inputType === "checkbox" ? evt.target.checked : evt.target.value;
132
+ }
133
+
134
+ updateParentData(generalValue, aiSuggestionAccepted);
135
+
80
136
  } else {
81
137
  setTextValue({ value: evt.target.value });
82
138
  inputOnChange && inputOnChange(evt);
83
139
  }
84
140
  };
85
141
 
142
+ useEffect(() => {
143
+
144
+ if(!isCreators) return;
145
+
146
+ if(Object.keys(suggestions).length === 0) return;
147
+
148
+ if(currentSuggestion?.[inputId]) return
149
+
150
+
151
+ const firstSuggestion = suggestions?.[inputId]?.[0];
152
+
153
+ if(!firstSuggestion) return;
154
+
155
+ setCurrentSuggestionValue({
156
+ inputId,
157
+ index: 0,
158
+ value: firstSuggestion?.value
159
+ });
160
+
161
+ }, [suggestions]);
162
+
163
+ useEffect(() => {
164
+ if(!isAiActive || !isCreators) return;
165
+ setAiSuggestionAccepted(false);
166
+ }, [isAiActive]);
167
+
168
+ useEffect(() => {
169
+ if(!isCreators) return;
170
+ if(!isAiActive && !aiSuggestionAccepted) {
171
+ setTextValue({ value: valueAccepted });
172
+ }
173
+ }, [suggestions, isAiActive]);
174
+
86
175
  useEffect(() => {
87
176
  setRequiredEmpty(
88
177
  isRequired &&
@@ -90,14 +179,26 @@ export const GeneralInput = ({
90
179
  );
91
180
  }, [textValue]);
92
181
 
93
- const numberInputOnWheelPreventChange = (e) => {
94
- // Prevent the input value change
95
- e.target.blur();
96
- // Prevent the page/container scrolling
97
- e.stopPropagation();
98
- // Refocus immediately, on the next tick (after the current function is done)
99
- setTimeout(() => e.target.focus(), 0);
100
- };
182
+ // const numberInputOnWheelPreventChange = (e) => {
183
+ // // Prevent the input value change
184
+ // e.target.blur();
185
+ // // Prevent the page/container scrolling
186
+ // e.stopPropagation();
187
+ // // Refocus immediately, on the next tick (after the current function is done)
188
+ // setTimeout(() => e.target.focus(), 0);
189
+ // };
190
+
191
+ //AI Generation
192
+
193
+ const handleAcceptSuggestion = (suggestionValue) => {
194
+ if(!suggestionValue || !isCreators) return;
195
+
196
+ setValueAccepted(suggestionValue);
197
+ setAiSuggestionAccepted(true);
198
+ setIsAiActive(false);
199
+
200
+ updateParentData(suggestionValue, true, suggestionValue);
201
+ }
101
202
 
102
203
  return (
103
204
  <Container isRequired={requiredEmpty} className={auditClass}>
@@ -124,33 +225,113 @@ export const GeneralInput = ({
124
225
  disabled={disabled}
125
226
  />
126
227
  ) : inputType !== "textarea" ? (
127
- <input
128
- type={inputType}
129
- disabled={disabled}
130
- id={inputId}
131
- size={inputSize}
132
- className="general-input"
133
- placeholder={inputPlaceholder}
134
- value={textValue.value}
135
- onInput={(e) => onHandleChange(e)}
136
- maxLength={maxChar}
137
- required={isRequired}
138
- onKeyDown={onKeyDown}
139
- onWheel={numberInputOnWheelPreventChange}
140
- />
228
+ <div>
229
+ <InputContainer className={hasAiGeneration ? "ai-generation" : ""}>
230
+ <input
231
+ type={inputType}
232
+ disabled={disabled}
233
+ id={inputId}
234
+ size={inputSize}
235
+ className={`general-input ${isAiActive && isBenefitInput && "ia-input"}`}
236
+ placeholder={inputPlaceholder}
237
+ value={isAiActive ? (
238
+ currentSuggestion?.[inputId]?.value
239
+ ) : textValue.value}
240
+ onInput={(e) => onHandleChange(e)}
241
+ maxLength={maxChar}
242
+ required={isRequired}
243
+ onKeyDown={onKeyDown}
244
+ // onWheel={numberInputOnWheelPreventChange}
245
+ />
246
+ {
247
+
248
+ hasAiGeneration && isBenefitInput && (
249
+ <div className={`icon_container ${isAiAvailable ? "ai-available" : ''} ${isAiActive ? 'ai-active' : ''}`} title={!isAiAvailable ? 'Debes de completar ficha técnica e imágenes para desbloquear la generación con IA' : ''} onClick={() => {
250
+ handlerAiGeneration({ type: "attribute" })
251
+ }}>
252
+ <img className={`${isAiGenerationLoading ? 'loading' : ''}`} src={isAiGenerationLoading ? LoadingIcon : isAiActive ? CancelIcon : AiGenerationIcon} />
253
+ </div>
254
+ )
255
+
256
+ }
257
+ </InputContainer>
258
+ <BottomContainer className={isAiActive ? "with-ai" : ""}>
259
+ {
260
+ isAiActive && (
261
+ <div className="ai-options">
262
+
263
+ <OptionsContainer>
264
+ <div className={
265
+ `arrow ${currentSuggestion?.[inputId]?.index === 0 && "disabled"}`
266
+ } onClick={() => {
267
+ handleChangeSuggestion({ action: "prev" })
268
+ }}>
269
+ <img src={ArrowIcon} alt="" />
270
+ </div>
271
+ <p>
272
+ {(currentSuggestion?.[inputId]?.index + 1) || 1}
273
+ /
274
+ {suggestions?.[inputId]?.length}
275
+ </p>
276
+ <div className={`arrow right ${currentSuggestion?.[inputId]?.index === suggestions?.[inputId]?.length - 1 && "disabled"}`} onClick={() => {
277
+
278
+ }}>
279
+ <img onClick={() => {
280
+ handleChangeSuggestion({ action: "next" })
281
+ }} src={ArrowIcon} alt="ai icon" />
282
+ </div>
283
+ </OptionsContainer>
284
+
285
+ <ButtonsContainer>
286
+
287
+ <div className={`reload-suggestions ${isAiRegenerationLoading && "loading"}`} onClick={() => {
288
+ handlerRegenerateSuggestions({
289
+ type: "attribute"
290
+ });
291
+ }}>
292
+ <img className="" src={isAiRegenerationLoading ? LoadingIcon : ReloadIcon} />
293
+ </div>
294
+
295
+ <div className="accept-suggestion" onClick={() => {
296
+ handleAcceptSuggestion(currentSuggestion?.[inputId]?.value);
297
+ }}>
298
+ <p>Aceptar sugerencia</p>
299
+ </div>
300
+
301
+ </ButtonsContainer>
302
+
303
+ </div>
304
+ )
305
+ }
306
+ </BottomContainer>
307
+ </div>
141
308
  ) : (
142
309
  <InputFormatter
143
310
  name={inputName}
144
- inputId={inputId}
145
- placeholder={inputPlaceholder}
146
- mainValue={textValue.value}
147
- onChange={onHandleChange}
148
- articleId={articleId}
149
- updatedDescriptions={updatedDescriptions}
150
- setUpdatedDescriptions={setUpdatedDescriptions}
151
- maxChar={maxChar}
152
- isRequired={isRequired}
153
- disabled={disabled}
311
+ inputId={inputId}
312
+ placeholder={inputPlaceholder}
313
+ mainValue={textValue.value}
314
+ onChange={onHandleChange}
315
+ articleId={articleId}
316
+ updatedDescriptions={updatedDescriptions}
317
+ setUpdatedDescriptions={setUpdatedDescriptions}
318
+ maxChar={maxChar}
319
+ isRequired={isRequired}
320
+ disabled={disabled}
321
+ hasAiGeneration={hasAiGeneration && inputId != 'commentary-box'}
322
+ isAiAvailable={isAiAvailable}
323
+ aiGenerated={aiGenerated}
324
+ handlerAiGeneration={() => {
325
+ handlerAiGeneration({ type: "description" })
326
+ }}
327
+ handlerRegenerateSuggestions={() => {
328
+ handlerRegenerateSuggestions({ type: "description" })
329
+ }}
330
+ handleChangeSuggestion={handleChangeSuggestion}
331
+ isAiGenerationLoading={isAiGenerationLoading}
332
+ isAiRegenerationLoading={isAiRegenerationLoading}
333
+ isAiActive={isAiActive}
334
+ setIsAiActive={setIsAiActive}
154
335
  />
155
336
  )}
156
337
  {/* <p>{description}</p> */}
@@ -84,3 +84,84 @@ export const Container = styled.div`
84
84
  background: lightgray;
85
85
  }
86
86
  `;
87
+
88
+ export const InputContainer = styled.div`
89
+
90
+ &.ai-generation {
91
+ position: relative;
92
+
93
+ cursor: pointer;
94
+
95
+ .ia-input {
96
+ padding-right: 2.5rem;
97
+ }
98
+
99
+ .icon_container {
100
+ position: absolute;
101
+ right: 10px;
102
+ top: 50%;
103
+ transform: translateY(-50%);
104
+ width: 20px;
105
+ height: 20px;
106
+ padding: 4px;
107
+
108
+ border-radius: 50%;
109
+ overflow: hidden;
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ z-index: 1;
114
+
115
+ &::before {
116
+ content: '';
117
+ position: absolute;
118
+ top: 0;
119
+ left: 0;
120
+ width: 100%;
121
+ height: 100%;
122
+
123
+ background: linear-gradient(
124
+ 120deg,
125
+ #ffffff 10%,
126
+ #ffe0f4 50%,
127
+ #ffffff 90%
128
+ );
129
+ background-size: 200% 200%;
130
+
131
+ animation: ai-shimmer 3s ease-in-out infinite alternate;
132
+ z-index: -1;
133
+ }
134
+
135
+ &.ai-available::before{
136
+
137
+ background: gray;
138
+ background-size: 200% 200%;
139
+
140
+ }
141
+
142
+ img {
143
+ width: 100%;
144
+ height: 100%;
145
+ object-fit: contain;
146
+ position: relative;
147
+ z-index: 2;
148
+ }
149
+
150
+ img.loading {
151
+ width: 20px;
152
+ height: 20px;
153
+ }
154
+
155
+ }
156
+ }
157
+
158
+ @keyframes ai-shimmer {
159
+ 0% {
160
+ background-position: 0% 50%;
161
+ }
162
+ 100% {
163
+ background-position: 100% 50%;
164
+ }
165
+ }
166
+
167
+ `;