@omniumretail/component-library 1.0.50 → 1.0.52

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,254 @@
1
+ import styles from './styles.module.scss';
2
+ import { useEffect, useImperativeHandle, useState } from 'react';
3
+ import { useForm } from 'antd/es/form/Form';
4
+ import classNames from 'classnames';
5
+ import React from 'react';
6
+ import { t } from 'i18next';
7
+
8
+ interface CategoryResponse {
9
+ data: any;
10
+ serverReadyData: any;
11
+ onNextCategoryAvailabilityChange: (hasNext: boolean) => void;
12
+ onPreviousCategoryAvailabilityChange: (hasPrevious: boolean) => void;
13
+ };
14
+
15
+ type Category = {
16
+ Title: string;
17
+ Key: string;
18
+ Data: any;
19
+ Children?: Category[];
20
+ };
21
+
22
+ const updateCategoryAnswers = (CategoryAnswers: any[], categoryToUpdate: any, updatedQuestions: any) => {
23
+ CategoryAnswers.forEach((category: any) => {
24
+ if (category.Data.CategoryId === categoryToUpdate.CategoryId) {
25
+ category.Data.Questions = category.Data.Questions.map((question: any, index: any) => {
26
+ return {
27
+ ...question,
28
+ Answer: updatedQuestions[index].Answer
29
+ };
30
+ });
31
+ }
32
+ else if (category.Children && category.Children.length > 0) {
33
+ updateCategoryAnswers(category.Children, categoryToUpdate, updatedQuestions);
34
+ }
35
+ });
36
+ return CategoryAnswers;
37
+ };
38
+
39
+ const findCategoryWithQuestions = (currentKey: string, categories: Category[], direction: 'next' | 'prev'): Category | null => {
40
+ let foundCurrent = false;
41
+
42
+ const searchCategories = (categoryList: Category[], direction: 'next' | 'prev'): Category | null => {
43
+ const iterable = direction === 'next' ? categoryList : [...categoryList].reverse();
44
+
45
+ for (const category of iterable) {
46
+ if (foundCurrent) {
47
+ if (category.Data.Questions.length > 0) {
48
+ return category;
49
+ }
50
+ } else if (category.Key === currentKey) {
51
+ foundCurrent = true;
52
+ }
53
+
54
+ if (category.Children) {
55
+ const result = searchCategories(category.Children, direction);
56
+ if (result) {
57
+ return result;
58
+ }
59
+ }
60
+ }
61
+ return null;
62
+ };
63
+
64
+ return searchCategories(categories, direction);
65
+ };
66
+
67
+ const getCategoryObject = (currentKey: string, categories: Category[], direction: 'next' | 'prev'): any => {
68
+ const category = findCategoryWithQuestions(currentKey, categories, direction);
69
+ return category ? category.Data : null;
70
+ };
71
+
72
+ const hasCategory = (currentKey: string, categories: Category[], direction: 'next' | 'prev'): boolean => {
73
+ const category = findCategoryWithQuestions(currentKey, categories, direction);
74
+ return !!category;
75
+ };
76
+
77
+ export const CategoryReadOnly = React.forwardRef((props: CategoryResponse, ref) => {
78
+ const { data } = props;
79
+
80
+ const [currentKey, setCurrentKey] = useState(data.CategoryAnswers[0].Key);
81
+ const [localData, setLocalData] = useState<any>(data);
82
+ // Setting first set of questions as default open
83
+ const [selectedCategory, setSelectedCategory] = useState<any>(data.CategoryAnswers[0]);
84
+ const [initialValues, setInitialValues] = useState<any>(data.CategoryAnswers[0].Data);
85
+ const [form] = useForm();
86
+
87
+
88
+ const updateInitialValues = (data: any) => {
89
+ const {
90
+ Questions,
91
+ OpenAnswer
92
+ } = data;
93
+
94
+ const initial = {
95
+ Questions: Questions.map((question: any) => ({
96
+ Subject: question.Subject,
97
+ Description: question.Description,
98
+ Answer: question.Answer || ""
99
+ })),
100
+ OpenAnswer: OpenAnswer
101
+ };
102
+
103
+ setInitialValues(initial);
104
+ form.setFieldsValue(initial);
105
+ };
106
+
107
+ const handleLabelClick = (category: any, index: number) => {
108
+ setSelectedCategory(category);
109
+ updateInitialValues(category.Data);
110
+ setCurrentKey(index);
111
+ };
112
+
113
+ const onFinish = (values: any) => {
114
+ const updatedQuestions = initialValues.Questions.map((question: any, index: number) => {
115
+ return {
116
+ ...question,
117
+ Answer: values.Questions[index].Answer
118
+ };
119
+ });
120
+
121
+ const updatedCategory = updateCategoryAnswers(localData.CategoryAnswers, selectedCategory.Data, updatedQuestions);
122
+
123
+ const updatedLocalData = { ...localData, CategoryAnswers: updatedCategory };
124
+
125
+ setLocalData(updatedLocalData);
126
+ };
127
+
128
+ const renderCategories = (categories: any) => {
129
+ return categories.map((category: any, index: number) => {
130
+ const labelClasses = classNames({
131
+ [styles.cursorPointer]: category.Data.Questions.length > 0
132
+ }, [styles.label]);
133
+
134
+ const indexCheck = category.Data.Questions.length > 0 && category.Key;
135
+
136
+ return (
137
+ <div
138
+ className={`${styles.labelWrapper} ${category.Data.CategoryId === selectedCategory.Data.CategoryId ? styles.active : ""}`}
139
+ key={index}
140
+ >
141
+ <div
142
+ className={labelClasses}
143
+ data-index={indexCheck}
144
+ onClick={() => category.Data.Questions.length > 0 && handleLabelClick(category, indexCheck)}
145
+ >
146
+ {category.Title}
147
+ </div>
148
+ {category.Children && (
149
+ <div className={styles.subCategory}>
150
+ {renderCategories(category.Children)}
151
+ </div>
152
+ )}
153
+ </div>
154
+ )
155
+ });
156
+ };
157
+
158
+ const handleNextClick = () => {
159
+ const nextCategory = findCategoryWithQuestions(currentKey, localData.CategoryAnswers, 'next');
160
+ if (nextCategory) {
161
+ setCurrentKey(nextCategory.Key);
162
+ setSelectedCategory(nextCategory);
163
+ }
164
+
165
+ const nextCategoryData = getCategoryObject(currentKey, localData.CategoryAnswers, 'next');
166
+ setSelectedCategory(nextCategoryData ? { ...nextCategory, data: nextCategoryData } : null);
167
+ updateInitialValues(nextCategoryData);
168
+ };
169
+
170
+ const handlePreviousClick = () => {
171
+ const prevCategory = findCategoryWithQuestions(currentKey, localData.CategoryAnswers, 'prev');
172
+ if (prevCategory) {
173
+ setCurrentKey(prevCategory.Key);
174
+ setSelectedCategory(prevCategory);
175
+ }
176
+
177
+ const prevCategoryData = getCategoryObject(currentKey, localData.CategoryAnswers, 'prev');
178
+ setSelectedCategory(prevCategoryData ? { ...prevCategory, data: prevCategoryData } : null);
179
+ updateInitialValues(prevCategoryData);
180
+ };
181
+
182
+ useImperativeHandle(ref, () => ({
183
+ handleNextClick,
184
+ handlePreviousClick,
185
+ }));
186
+
187
+ useEffect(() => {
188
+ props.serverReadyData(localData);
189
+ }, [localData]);
190
+
191
+ useEffect(() => {
192
+ const hasNext = hasCategory(currentKey, localData.CategoryAnswers, 'next');
193
+ props.onNextCategoryAvailabilityChange(hasNext);
194
+
195
+ const hasPrevious = hasCategory(currentKey, localData.CategoryAnswers, 'prev');
196
+ props.onPreviousCategoryAvailabilityChange(hasPrevious);
197
+ }, [currentKey, localData.CategoryAnswers, props]);
198
+
199
+ const questionWrapperClasses = classNames({
200
+ [styles.questionWrapperOpenAnswer]: selectedCategory.Data.OpenAnswer,
201
+ }, [styles.questionWrapper]);
202
+
203
+ const answerClasses = classNames({
204
+ [styles.answerOpenAnswer]: selectedCategory.Data.OpenAnswer,
205
+ }, [styles.answer]);
206
+
207
+ return (
208
+ <div className={styles.categoryResponse}>
209
+ <div className={styles.sidebarWrapper}>
210
+ <div className={styles.title}>{t('components.categoryReadOnly.categories')}</div>
211
+ {renderCategories(localData.CategoryAnswers)}
212
+ </div>
213
+ <div className={styles.contentWrapper}>
214
+
215
+ <div className={styles.details}>
216
+ <div className={styles.categoryName}>
217
+ {selectedCategory?.Data?.CategoryName}
218
+ </div>
219
+ <div className={styles.categoryGradesWrapper}>
220
+ <div className={styles.categoryGradesTitle}>
221
+ {t('components.categoryReadOnly.categoryAverage')}
222
+ </div>
223
+ <div className={styles.categoryGrades}>
224
+ <div className={styles.gradeBox} style={{ backgroundColor: localData.UserColor }}>
225
+ {selectedCategory?.Data.Score}
226
+ </div>
227
+ <div className={styles.gradeBox} style={{ backgroundColor: localData.SupervisorColor }}>
228
+ {selectedCategory?.Data.SupervisorScore}
229
+ </div>
230
+ </div>
231
+ </div>
232
+ </div>
233
+
234
+ {selectedCategory?.Data?.Questions.map((question: any, index: number) => (
235
+ <div className={questionWrapperClasses} key={index}>
236
+ <div className={styles.question}>
237
+ <div className={styles.subject}>
238
+ {question.Subject}
239
+ </div>
240
+ </div>
241
+ <div className={answerClasses}>
242
+ <div className={styles.gradeBox} style={{ backgroundColor: localData.UserColor }}>
243
+ {question.UserAnswer}
244
+ </div>
245
+ <div className={styles.gradeBox} style={{ backgroundColor: localData.SupervisorColor }}>
246
+ {question.SupervisorAnswer}
247
+ </div>
248
+ </div>
249
+ </div>
250
+ ))}
251
+ </div>
252
+ </div>
253
+ );
254
+ });
@@ -0,0 +1,184 @@
1
+ .categoryResponse {
2
+ display: grid;
3
+ grid-template-columns: 300px auto;
4
+ gap: 16px;
5
+ height: 100%;
6
+ }
7
+
8
+ .sidebarWrapper {
9
+ overflow: auto;
10
+ background: #EBECED;
11
+ }
12
+
13
+ .contentWrapper {
14
+ overflow: auto;
15
+ background: var(--color-white);
16
+ }
17
+
18
+ .sidebarWrapper,
19
+ .contentWrapper {
20
+ padding: 20px;
21
+ }
22
+
23
+ .title {
24
+ font-size: var(--font-size-body-4);
25
+ color: var(--color-blue);
26
+ margin-bottom: 36px;
27
+ font-weight: var(--font-weight-semibold);
28
+ text-transform: uppercase;
29
+
30
+ }
31
+
32
+ .label {
33
+ padding-bottom: 15px;
34
+ font-weight: var(--font-weight-bold);
35
+ font-size: var(--font-size-body-4);
36
+ }
37
+
38
+ .cursorPointer {
39
+ cursor: pointer;
40
+ }
41
+
42
+ .subCategory {
43
+ padding-left: 16px;
44
+
45
+ .label {
46
+ font-weight: var(--font-weight-semibold);
47
+ }
48
+
49
+ .labelWrapper {
50
+ .subCategory {
51
+ .label {
52
+ font-weight: var(--font-weight-light);
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ .labelWrapper {
59
+ display: block;
60
+ }
61
+
62
+ .active {
63
+ color: var(--color-orange);
64
+ }
65
+
66
+ .details {
67
+ margin-bottom: 32px;
68
+ display: flex;
69
+ justify-content: space-between;
70
+ align-items: center;
71
+ }
72
+
73
+ .categoryName {
74
+ font-size: var(--font-size-body-4);
75
+ color: var(--color-blue);
76
+ font-weight: var(--font-weight-semibold);
77
+ margin-bottom: 8px;
78
+ }
79
+
80
+ .categoryDescription {
81
+ font-size: var(--font-size-body-3);
82
+ font-weight: var(--font-weight-light);
83
+ color: var(--color-black);
84
+ }
85
+
86
+ // Questions
87
+ .questionWrapper {
88
+ display: flex;
89
+ flex-direction: row;
90
+ gap: 36px;
91
+ margin-bottom: 36px;
92
+ border-bottom: 1px solid rgba(var(--color-blue-rgb), .2);
93
+ padding-bottom: 4px;
94
+ }
95
+
96
+ .questionWrapperOpenAnswer {
97
+ flex-direction: column;
98
+ border-bottom: 1px solid rgba(var(--color-blue-rgb), .2);
99
+ margin-bottom: 24px;
100
+
101
+ .question {
102
+ width: 100%;
103
+ border-bottom: none;
104
+ }
105
+
106
+ .answer {
107
+ width: 100%;
108
+ height: auto;
109
+
110
+ :global {
111
+ .ant-input {
112
+ min-height: 140px;
113
+ }
114
+
115
+ .ant-form-item {
116
+ margin-bottom: 16px;
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ .question {
123
+ width: calc(100% - 149px);
124
+ display: flex;
125
+ align-items: center;
126
+ }
127
+
128
+ .answer {
129
+ width: 130px;
130
+ min-width: 130px;
131
+ align-self: flex-end;
132
+ display: flex;
133
+ justify-content: space-between;
134
+ padding-right: 15px;
135
+ }
136
+
137
+ .answerOpenAnswer {
138
+ display: none;
139
+ }
140
+
141
+ .categoryGrades {
142
+ width: 130px;
143
+ min-width: 130px;
144
+ align-self: flex-end;
145
+ display: flex;
146
+ justify-content: space-between;
147
+ gap: 34px;
148
+ }
149
+
150
+ .subject {
151
+ font-size: var(--font-size-body-3);
152
+ font-weight: var(--font-weight-medium);
153
+ }
154
+
155
+ .description {
156
+ font-weight: var(--font-weight-light);
157
+ margin-bottom: 4px;
158
+ }
159
+
160
+ .gradeBox {
161
+ padding: 6px;
162
+ font-size: var(--font-size-body-4);
163
+ font-weight: var(--font-weight-medium);
164
+ color: var(--color-white);
165
+ border-radius: 5px;
166
+ display: inline-flex;
167
+ letter-spacing: 3px;
168
+ }
169
+
170
+ .categoryGradesWrapper {
171
+ background-color: rgba(217, 217, 217, 0.5);
172
+ border-radius: 5px;
173
+ padding: 8px 15px;
174
+ display: flex;
175
+ flex-direction: row;
176
+ gap: 50px;
177
+ align-items: center;
178
+ }
179
+
180
+ .categoryGradesTitle {
181
+ font-size: var(--font-size-body-3);
182
+ font-weight: var(--font-weight-medium);
183
+ color: var(--color-black);
184
+ }
@@ -7,10 +7,12 @@
7
7
 
8
8
  .sidebarWrapper {
9
9
  background: #EBECED;
10
+ overflow: auto;
10
11
  }
11
12
 
12
13
  .contentWrapper {
13
14
  background: var(--color-white);
15
+ overflow: auto;
14
16
  }
15
17
 
16
18
  .sidebarWrapper,
@@ -1,6 +1,6 @@
1
1
  import { Button } from '../Button';
2
2
  import { Modal } from 'antd';
3
- import { useEffect, useRef, useState } from 'react';
3
+ import { useEffect, useState } from 'react';
4
4
  import { CloseOutlined } from '@ant-design/icons';
5
5
  import styles from './styles.module.scss';
6
6
  import { TagField } from '../Tag';
@@ -26,104 +26,60 @@ export const ModalWithTable = (props: ModalWithTableProps) => {
26
26
  modalData,
27
27
  isLoading,
28
28
  } = props;
29
-
30
- const prevIsOpenRef = useRef<boolean>();
31
- useEffect(() => {
32
- prevIsOpenRef.current = isOpen;
33
- }, [isOpen]);
34
- const prevIsOpen = prevIsOpenRef.current;
35
-
36
- // Add a new state to store the initial values
37
- const [initialValues, setInitialValues] = useState<any>({
38
- pageInfo: {},
39
- rowSelectionInfo: [],
40
- tagsInfo: [],
41
- selectedData: [],
42
- });
29
+
43
30
  const pageBase = {currentPage: 1};
44
31
  const [open, setOpen] = useState((isOpen));
45
32
  const [confirmLoading, setConfirmLoading] = useState(false);
46
33
  const [pageInfo, setPageInfo] = useState<any>(pageBase);
47
34
  const [rowSelectionInfo, setRowSelectionInfo] = useState<any>();
48
35
  const [tagsInfo, setTagsInfo] = useState<any>([]);
36
+ const [ModalCanceled, setModalCanceled] = useState<boolean>(false);
49
37
  const [selectedData, setSelectedData] = useState<any>([]);
50
38
 
51
39
  useEffect(()=> {
52
40
  setOpen(isOpen);
53
-
54
- if (isOpen && !prevIsOpen) {
55
- setInitialValues({
56
- pageInfo: pageInfo,
57
- rowSelectionInfo: rowSelectionInfo,
58
- tagsInfo: tagsInfo,
59
- selectedData: selectedData,
60
- });
61
- }
62
41
  }, [isOpen])
63
42
 
64
43
  const hideModal = () => {
65
44
  setOpen(false);
66
- setPageInfo(initialValues.pageInfo);
67
- setRowSelectionInfo(initialValues.rowSelectionInfo);
68
- setTagsInfo(initialValues.tagsInfo);
69
- setSelectedData(initialValues.selectedData);
45
+ setModalCanceled(true);
70
46
  };
71
47
 
72
48
  const saveChanges = () => {
73
49
  setOpen(false);
50
+ setModalCanceled(false);
74
51
  };
75
52
 
76
53
  useEffect(() => {
77
- if (!open) {
54
+ if(!open) {
78
55
  setPageInfo(pageBase);
79
56
  }
80
-
81
- const modalDataObj = {
82
- pageInfo: pageInfo,
83
- rowSelectionInfo: rowSelectionInfo,
84
- tagsInfo: tagsInfo,
85
- open: open,
86
- selectedData: selectedData,
87
- };
88
-
89
- modalData(modalDataObj);
90
- }, [tagsInfo, open, rowSelectionInfo, selectedData]);
57
+
58
+ ModalCanceled
59
+ ? modalData({
60
+ "pageInfo": {},
61
+ "rowSelectionInfo": [],
62
+ "tagsInfo": [],
63
+ "open": open,
64
+ "selectedData": []
65
+ })
66
+ : modalData({
67
+ "pageInfo": pageInfo,
68
+ "rowSelectionInfo": rowSelectionInfo,
69
+ "tagsInfo": tagsInfo,
70
+ "open": open,
71
+ "selectedData": selectedData
72
+ });
73
+ }, [tagsInfo, open]);
91
74
 
92
75
  useEffect(() => {
93
76
  const selectedRowKeys = rowSelectionInfo || tableData.rowSelection.selectedRowKeys;
94
77
 
95
- if(tableData?.rowSelection?.type === 'radio') {
96
- const filteredArray = tableData?.dataSource?.filter((element) => {
97
- return selectedRowKeys.includes(element[tableData.rowKeyValue as any]);
98
- });
99
-
100
- setSelectedData(filteredArray);
101
- return;
102
- }
103
-
104
- setSelectedData((prevData: any) => {
105
- const newData = tableData?.dataSource?.filter((element) => {
106
- return selectedRowKeys.includes(element[tableData.rowKeyValue as any]);
107
- }) || [];
108
-
109
- const oldData = prevData.filter((dataItem: any) => {
110
- return !tableData?.dataSource?.some(
111
- (element) => element[tableData.rowKeyValue as any] === dataItem[tableData.rowKeyValue as any]
112
- );
113
- });
114
-
115
- const combinedData = [...oldData, ...newData].filter((dataItem, index, self) => {
116
- return (
117
- index ===
118
- self.findIndex(
119
- (item) => item[tableData.rowKeyValue as any] === dataItem[tableData.rowKeyValue as any]
120
- )
121
- );
122
- });
123
-
124
- setInitialValues({...initialValues, selectedData: combinedData});
125
- return combinedData;
78
+ const filteredArray = tableData?.dataSource?.filter((element) => {
79
+ return selectedRowKeys.includes(element[tableData.rowKeyValue as any]);
126
80
  });
81
+
82
+ setSelectedData(filteredArray);
127
83
  }, [rowSelectionInfo])
128
84
 
129
85
  useEffect(()=> {
@@ -165,7 +121,7 @@ export const ModalWithTable = (props: ModalWithTableProps) => {
165
121
  <div className={styles.tagsWrapper}>
166
122
  <TagField tagsInfo={setTagsInfo}/>
167
123
  </div>
168
-
124
+
169
125
  <div className={styles.tableWrapper}>
170
126
  { tableData &&
171
127
  <Table {...tableData} paginationInfo={setPageInfo} rowSelectionInfo={setRowSelectionInfo} headingTranslationsKey={'tableHeadings'} />
@@ -176,4 +132,3 @@ export const ModalWithTable = (props: ModalWithTableProps) => {
176
132
  </>
177
133
  )
178
134
  }
179
-
@@ -58,20 +58,7 @@ export const Table = (props: TableCustomProps) => {
58
58
  const [selectedRowKeys, setselectedRowKeys] = useState<any>(rowSelection?.selectedRowKeys || []);
59
59
 
60
60
  const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
61
- if(rowSelection.type === 'radio') {
62
- setselectedRowKeys(newSelectedRowKeys);
63
- return;
64
- }
65
-
66
- setselectedRowKeys((prevKeys: any) => {
67
- const dataSourceIds = dataSource?.map((data) => data[rowKeyValue as any] )
68
-
69
- const oldKeys = prevKeys.filter((key: any) => {
70
- return !dataSourceIds?.includes(key)
71
- });
72
-
73
- return [...oldKeys, ...newSelectedRowKeys];
74
- });
61
+ setselectedRowKeys(newSelectedRowKeys);
75
62
  };
76
63
 
77
64
  const handleChange: TableProps<any>['onChange'] = (pagination) => {
@@ -98,7 +85,7 @@ export const Table = (props: TableCustomProps) => {
98
85
  }, [selectedRowKeys]);
99
86
 
100
87
  useEffect(() => {
101
- if (dataSource && (dataSource as any)?.length > 0) {
88
+ if (dataSource) {
102
89
  // Columns
103
90
  let columns = Object.keys(dataSource?.[0]).map(key => {
104
91
  if (hiddenColumns?.includes(key)) {
@@ -21,3 +21,4 @@ export * from './AnalyticsBar';
21
21
  export * from './CategoryResponse';
22
22
  export * from './Upload';
23
23
  export * from './UserInfo';
24
+ export * from './CategoryReadOnly';
@@ -30,6 +30,10 @@
30
30
  "categoryResponse": {
31
31
  "notApplicable": "Not Applicable",
32
32
  "answer": "Answer"
33
+ },
34
+ "categoryReadOnly": {
35
+ "categories": "Categories",
36
+ "categoryAverage": "Category Average"
33
37
  }
34
38
  }
35
39
  }
@@ -30,6 +30,10 @@
30
30
  "categoryResponse": {
31
31
  "notApplicable": "Not Applicable ES",
32
32
  "answer": "Answer ES"
33
+ },
34
+ "categoryReadOnly": {
35
+ "categories": "Categories ES",
36
+ "categoryAverage": "Category Average ES"
33
37
  }
34
38
  }
35
39
  }
@@ -48,6 +48,10 @@
48
48
  "categoryResponse": {
49
49
  "notApplicable": "Não Aplicável",
50
50
  "answer": "Resposta"
51
+ },
52
+ "categoryReadOnly": {
53
+ "categories": "Categories PT",
54
+ "categoryAverage": "Category Average PT"
51
55
  }
52
56
  }
53
57
  }