@omniumretail/component-library 1.0.20 → 1.0.21

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omniumretail/component-library",
3
- "version": "1.0.20",
3
+ "version": "1.0.21",
4
4
  "private": false,
5
5
  "main": "dist/bundle.js",
6
6
  "typings": "./dist/types/index",
@@ -0,0 +1,14 @@
1
+ import { Meta, Story } from "@storybook/react";
2
+ import { Category } from '.';
3
+
4
+ export default {
5
+ title: 'Category',
6
+ component: Category,
7
+ } as Meta;
8
+
9
+ const Template: Story<any> = (args) => <Category {...args}></Category>;
10
+
11
+ export const Primary = Template.bind({});
12
+ Primary.args = {
13
+ };
14
+
@@ -0,0 +1,159 @@
1
+ import { Form, Select } from 'antd';
2
+ import { useForm } from 'antd/es/form/Form';
3
+ import { Button } from 'components/Button';
4
+ import { InputField } from 'components/Input';
5
+ import { Label } from 'components/Label';
6
+ import { Questions } from 'components/Questions';
7
+ import { Switch } from 'components/Switch';
8
+ import { useEffect, useState } from 'react';
9
+ import styles from './styles.module.scss';
10
+
11
+ interface categoryContent {
12
+ categoryContentShow: boolean;
13
+ categoryContentInfo: any;
14
+ categorySidebarData: any;
15
+ }
16
+
17
+ const defaultInitialValues = {
18
+ generalEvaluationLevel: "0",
19
+ questions: []
20
+ };
21
+
22
+ export const CategoryContent = (props: categoryContent) => {
23
+ const {
24
+ categoryContentShow,
25
+ categorySidebarData
26
+ } = props;
27
+
28
+ const [contentInfo, setContentInfo] = useState<any>();
29
+ const [switchStatus, setSwitchStatus] = useState<boolean>(false);
30
+ const [initialValues, setInitialValues] = useState<any>(categorySidebarData?.data || defaultInitialValues)
31
+ const [form] = useForm();
32
+
33
+ const onFinish = (values: any) => {
34
+ setContentInfo(values);
35
+ };
36
+
37
+ const switchHandler = (enabled: boolean) => {
38
+ setSwitchStatus(enabled);
39
+ }
40
+
41
+ useEffect(() => {
42
+ props.categoryContentInfo(contentInfo);
43
+ }, [contentInfo]);
44
+
45
+ useEffect(() => {
46
+ setInitialValues(categorySidebarData?.data?.data);
47
+ }, [categorySidebarData]);
48
+
49
+ useEffect(() => {
50
+ form.setFieldsValue(initialValues);
51
+ }, [form, initialValues]);
52
+
53
+ return (
54
+ <div className={styles.categoryContent}>
55
+ <div className={styles.title}>Editar Categoria</div>
56
+
57
+ {
58
+ !categoryContentShow
59
+ ? <div className={styles.uncheckedCategory}>
60
+ Please Select a Category on the sidebar
61
+ </div>
62
+
63
+ : <>
64
+ <Form
65
+ initialValues={initialValues}
66
+ name="dynamic_form_nest_item"
67
+ onFinish={onFinish}
68
+ autoComplete="off"
69
+ form={form}
70
+ >
71
+ <div className={styles.formHeader}>
72
+ <div className={styles.category}>
73
+ <Label isUppercase>
74
+ Nome Categoria
75
+ </Label>
76
+ <Form.Item
77
+ name={['categoryName']}
78
+ rules={[{ required: true, message: 'Category Name is Missing' }]}
79
+ >
80
+ <InputField placeholder={'Category Name'} />
81
+ </Form.Item>
82
+ </div>
83
+
84
+ {!categorySidebarData?.data?.children?.length &&
85
+ <>
86
+ <div className={styles.openAnswer}>
87
+ <Label isUppercase>
88
+ Resposta aberta
89
+ </Label>
90
+ <Form.Item
91
+ name={['openAnswer']}
92
+ >
93
+ <Switch onChange={(enabled: boolean) => switchHandler(enabled)} />
94
+ </Form.Item>
95
+ </div>
96
+
97
+ {
98
+ !switchStatus &&
99
+ <>
100
+ <div className={styles.generalEvaluationLevel}>
101
+ <Label isUppercase>
102
+ Nivel de avaliação Geral
103
+ </Label>
104
+ <Form.Item
105
+ name={['generalEvaluationLevel']}
106
+ >
107
+ <Select options={[
108
+ {
109
+ value: '0',
110
+ label: '1 a 5',
111
+ },
112
+ {
113
+ value: '1',
114
+ label: '1 a 4',
115
+ },
116
+ {
117
+ value: '2',
118
+ label: '1 a 5 ou “Não Aplicável”',
119
+ },
120
+ ]}
121
+ />
122
+ </Form.Item>
123
+ </div>
124
+
125
+ <div className={styles.grade}>
126
+ <Label isUppercase>
127
+ Ponderação
128
+ </Label>
129
+ <Form.Item
130
+ name={['grade']}
131
+ rules={[{ required: true, message: 'Grade is Missing' }]}
132
+ >
133
+ <InputField placeholder={'EX: 15%'} />
134
+ </Form.Item>
135
+ </div>
136
+ </>
137
+ }
138
+ </>
139
+ }
140
+ </div>
141
+
142
+ {!categorySidebarData?.data?.children?.length &&
143
+ <div className={styles.questionsWrapper}>
144
+ <Label isUppercase>
145
+ Questões
146
+ </Label>
147
+ <Form.Item>
148
+ <Questions></Questions>
149
+ </Form.Item>
150
+ </div>
151
+ }
152
+ </Form>
153
+
154
+ <Button onClick={() => form.submit()}>Submit</Button>
155
+ </>
156
+ }
157
+ </div>
158
+ );
159
+ };
@@ -0,0 +1,51 @@
1
+ .title {
2
+ font-size: var(--font-size-body-4);
3
+ font-weight: 600;
4
+ line-height: 100%;
5
+ color: var(--color-blue);
6
+ text-transform: uppercase;
7
+ margin-bottom: 32px;
8
+ }
9
+
10
+ .uncheckedCategory {
11
+ font-size: var(--font-size-body-2);
12
+ color: var(--color-black);
13
+ font-weight: 700;
14
+ text-transform: uppercase;
15
+ }
16
+
17
+ .formHeader {
18
+ display: grid;
19
+ grid-template-columns: minmax(200px, 1fr) auto 1fr auto;
20
+ gap: 46px;
21
+ }
22
+
23
+ .questionsWrapper {
24
+ margin-top: 16px;
25
+ }
26
+
27
+ Label {
28
+ margin-bottom: 8px;
29
+ }
30
+
31
+ .categoryContent {
32
+ :global {
33
+ .ant-select {
34
+ width: 100%;
35
+ }
36
+
37
+ .ant-select-selector {
38
+ border-color: rgba(var(--color-black-rgb), .5) !important;
39
+ height: 50px !important;
40
+ display: flex;
41
+ align-items: center;
42
+ }
43
+ .ant-select-arrow {
44
+ color: var(--color-black);
45
+ }
46
+
47
+ .ant-switch {
48
+ width: 30px;
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,246 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Tree } from 'antd';
3
+ import type { TreeProps } from 'antd/es/tree';
4
+ import styles from './styles.module.scss';
5
+ import { PlusOutlined } from '@ant-design/icons';
6
+ import { Button } from 'components/Button';
7
+
8
+ export interface Questions {
9
+ grade: string;
10
+ questions: string;
11
+ info: string;
12
+ }
13
+ export interface Data {
14
+ categoryName: string;
15
+ generalEvaluationLevel: string;
16
+ grade: string;
17
+ openAnswer: boolean;
18
+ questions: Questions[];
19
+ }
20
+ export interface DataNode {
21
+ title: string;
22
+ key: string;
23
+ data?: Data;
24
+ children?: DataNode[];
25
+ }
26
+
27
+ interface SidebarProps {
28
+ categoryContentData: any;
29
+ categorySidebarInfo: any;
30
+ data: DataNode[];
31
+ }
32
+
33
+ function get_element_by_index(data: DataNode[], index: string | number): DataNode | null {
34
+ for (const item of data) {
35
+ if (item.key === index) {
36
+ return item;
37
+ } else if (item.children) {
38
+ const result = get_element_by_index(item.children, index);
39
+ if (result !== null) {
40
+ return result;
41
+ }
42
+ }
43
+ }
44
+ return null;
45
+ }
46
+
47
+ function addPropertyByIndex(data: DataNode[], index: string | number, property: string, value: any): DataNode[] {
48
+ return data.map((item: DataNode) => {
49
+ if (item.key === index) {
50
+ return { ...item, [property]: value };
51
+ }
52
+ else if (item.children) {
53
+ return { ...item, children: addPropertyByIndex(item.children, index, property, value) };
54
+ }
55
+ return item;
56
+ });
57
+ }
58
+
59
+ export const CategorySidebar = (props: SidebarProps) => {
60
+ const {
61
+ categoryContentData,
62
+ data
63
+ } = props;
64
+
65
+ const [gData, setGData] = useState(data);
66
+ const [expandedKeys] = useState([]);
67
+ const [sidebarInfo, setSidebarInfo] = useState<any>();
68
+
69
+ const onDragEnter: TreeProps['onDragEnter'] = (info) => {
70
+ // expandedKeys
71
+ // setExpandedKeys(info.expandedKeys)
72
+ };
73
+
74
+ const onDrop: TreeProps['onDrop'] = (info) => {
75
+ const dropKey = info.node.key;
76
+ const dragKey = info.dragNode.key;
77
+ const dropPos = info.node.pos.split('-');
78
+ const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
79
+ const { data: nodeData } = info.node as DataNode;
80
+ const questions: Questions[] | undefined = nodeData?.questions;
81
+
82
+ if(questions && questions.length > 0) {
83
+ console.log('you cant add a subcategory to a category with questions');
84
+ return;
85
+ }
86
+
87
+ if (dropPos.length >= 4) {
88
+ console.log('cant have more than 3 levels');
89
+ return;
90
+ }
91
+
92
+ const loop = (
93
+ data: DataNode[],
94
+ key: React.Key,
95
+ callback: (node: DataNode, i: number, data: DataNode[]) => void,
96
+ ) => {
97
+ for (let i = 0; i < data.length; i++) {
98
+ if (data[i].key === key) {
99
+ return callback(data[i], i, data);
100
+ }
101
+ if (data[i].children) {
102
+ loop(data[i].children!, key, callback);
103
+ }
104
+ }
105
+ };
106
+ const data = [...gData];
107
+
108
+ // Find dragObject
109
+ let dragObj: DataNode;
110
+ loop(data, dragKey, (item, index, arr) => {
111
+ arr.splice(index, 1);
112
+ dragObj = item;
113
+ });
114
+
115
+ if (!info.dropToGap) {
116
+ // Drop on the content
117
+ loop(data, dropKey, (item) => {
118
+ item.children = item.children || [];
119
+ // where to insert
120
+ item.children.unshift(dragObj);
121
+ // {...dragObj, key: `${item.key}-${item.children.length}`}
122
+ });
123
+ } else if (
124
+ ((info.node as any).props.children || []).length > 0 && // Has children
125
+ (info.node as any).props.expanded && // Is expanded
126
+ dropPosition === 1 // On the bottom gap
127
+ ) {
128
+ loop(data, dropKey, (item) => {
129
+ item.children = item.children || [];
130
+ // where to insert
131
+ item.children.unshift(dragObj);
132
+ // in previous version, we use item.children.push(dragObj) to insert the
133
+ // item to the tail of the children
134
+ });
135
+ } else {
136
+ let ar: DataNode[] = [];
137
+ let i: number;
138
+ loop(data, dropKey, (_item, index, arr) => {
139
+ ar = arr;
140
+ i = index;
141
+ });
142
+ if (dropPosition === -1) {
143
+ ar.splice(i!, 0, dragObj!);
144
+ } else {
145
+ ar.splice(i! + 1, 0, dragObj!);
146
+ }
147
+ }
148
+
149
+ let newData: any = {};
150
+
151
+ // TODO: create a recursive function that does the same
152
+ // This code changes the indexes on the fly while dragging and dropping
153
+
154
+ let keyCounter = 0;
155
+ data.forEach((category: any) => {
156
+ const categoryKey = `${keyCounter++}`;
157
+ let children: any = [];
158
+ if (category.children) {
159
+ category.children.forEach((subcategory: any, subcategoryindex: number) => {
160
+ const subcategoryKey = `${categoryKey}.${subcategoryindex}`;
161
+ let subChildren: any = [];
162
+ if (subcategory.children) {
163
+ subcategory.children.forEach((lastSubcategory: any, lastSubCategoryIndex: number) => {
164
+ const lastSubcategoryKey = `${subcategoryKey}.${lastSubCategoryIndex}`;
165
+ subChildren.push({ ...lastSubcategory, key: lastSubcategoryKey });
166
+ });
167
+ }
168
+ children.push({ ...subcategory, children: subChildren, key: subcategoryKey });
169
+ });
170
+ }
171
+ newData[categoryKey] = { ...category, children };
172
+ });
173
+
174
+ // TODO: update this code if possible once the TODO before is done
175
+ /*
176
+ This code removes the property that was assigned to the object ex: 0: {}, 1: {} changes to {}, {}
177
+ */
178
+ const checkedData = Object.values(newData).map((category: any, index: number) => {
179
+ return { ...category, key: index };
180
+ });
181
+
182
+ setGData(checkedData);
183
+ };
184
+
185
+ const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
186
+ setSidebarInfo(selectedKeys);
187
+ };
188
+
189
+ const addCategory = () => {
190
+ setGData((prevCategories: DataNode[]) => [
191
+ ...prevCategories,
192
+ { title: `New category`, key: `${gData.length}` },
193
+ ])
194
+ }
195
+
196
+ useEffect(() => {
197
+ props.categorySidebarInfo({
198
+ selectKey: sidebarInfo,
199
+ data: get_element_by_index(gData, sidebarInfo && sidebarInfo[0]),
200
+ });
201
+ }, [sidebarInfo]);
202
+
203
+ useEffect(() => {
204
+ const newGData = addPropertyByIndex(gData, sidebarInfo && sidebarInfo[0], "data", categoryContentData);
205
+
206
+ const updatedGData = newGData.map((category: any, index) => {
207
+ if(category?.data) {
208
+ return {...category, title: `${category.data.categoryName} ${category.data.questions ? '(Q: ' + category.data.questions.length + ')' : ''} ${category.data.grade ? '(G: ' +category.data.grade + ')' : ''}`}
209
+ }
210
+ return category;
211
+ })
212
+
213
+ setGData(updatedGData);
214
+ }, [categoryContentData]);
215
+
216
+ useEffect(() => {
217
+ props.categorySidebarInfo({
218
+ selectKey: sidebarInfo,
219
+ data: get_element_by_index(gData, sidebarInfo && sidebarInfo[0])
220
+ });
221
+
222
+ console.log(gData);
223
+ }, [gData]);
224
+
225
+ return (
226
+ <div className={styles.categorySidebar}>
227
+ <div className={styles.title}>Categorias</div>
228
+ <Button icon={<PlusOutlined />} onClick={addCategory}>
229
+ Adicionar Categoria
230
+ </Button>
231
+
232
+ <div className={styles.categoryViewer}>
233
+ <Tree
234
+ className="draggable-tree"
235
+ defaultExpandedKeys={expandedKeys}
236
+ draggable
237
+ blockNode
238
+ onDragEnter={onDragEnter}
239
+ onDrop={onDrop}
240
+ treeData={gData}
241
+ onSelect={onSelect}
242
+ />
243
+ </div>
244
+ </div>
245
+ );
246
+ };
@@ -0,0 +1,24 @@
1
+ .categorySidebar {
2
+ :global {
3
+ .ant-tree-title {
4
+ font-weight: var(--font-weight-semibold);
5
+ }
6
+ }
7
+ }
8
+
9
+ .title {
10
+ font-size: var(--font-size-body-4);
11
+ font-weight: 600;
12
+ line-height: 100%;
13
+ color: var(--color-blue);
14
+ text-transform: uppercase;
15
+ }
16
+
17
+ button {
18
+ margin-top: 32px;
19
+ width: 100%;
20
+ }
21
+
22
+ .categoryViewer {
23
+ margin-top: 20px;
24
+ }
@@ -0,0 +1,58 @@
1
+ import styles from './styles.module.scss';
2
+ import { CategorySidebar, DataNode } from './CategorySidebar';
3
+ import { useEffect, useState } from 'react';
4
+ import { CategoryContent } from './CategoryContent';
5
+
6
+ interface Category {
7
+ data: DataNode;
8
+ }
9
+
10
+ const fakeData: any = {
11
+ data: [
12
+ {
13
+ "title": "21 (G: 2)",
14
+ "key": "0",
15
+ "data": {
16
+ "categoryName": "21",
17
+ "grade": "2"
18
+ }
19
+ },
20
+ {
21
+ "title": "asda (G: 5)",
22
+ "key": "1",
23
+ "data": {
24
+ "categoryName": "asda",
25
+ "grade": "5"
26
+ }
27
+ }
28
+ ],
29
+ noData: []
30
+ }
31
+
32
+ export const Category = (props: Category) => {
33
+ const {
34
+ data = fakeData.noData
35
+ } = props;
36
+
37
+ const [categoryContentInfo, setCategoryContentInfo] = useState<any>();
38
+ const [categorySidebarInfo, setCategorySidebarInfo] = useState<any>();
39
+ const [showContent, setShowContent] = useState<boolean>(false);
40
+
41
+ useEffect(() => {
42
+ }, [categoryContentInfo]);
43
+
44
+ useEffect(() => {
45
+ setShowContent(categorySidebarInfo?.selectKey?.length > 0);
46
+ }, [categorySidebarInfo]);
47
+
48
+ return (
49
+ <div className={styles.category}>
50
+ <div className={styles.sidebarWrapper}>
51
+ <CategorySidebar categorySidebarInfo={setCategorySidebarInfo} categoryContentData={categoryContentInfo} data={data} />
52
+ </div>
53
+ <div className={styles.contentWrapper}>
54
+ <CategoryContent categoryContentInfo={setCategoryContentInfo} categoryContentShow={showContent} categorySidebarData={categorySidebarInfo} />
55
+ </div>
56
+ </div>
57
+ )
58
+ };
@@ -0,0 +1,12 @@
1
+ .category {
2
+ display: grid;
3
+ grid-template-columns: 300px auto;
4
+ gap: 16px;
5
+ }
6
+
7
+ .sidebarWrapper,
8
+ .contentWrapper {
9
+ background: white;
10
+ padding: 20px;
11
+ }
12
+
@@ -4,7 +4,6 @@ import type { RadioGroupProps } from 'antd/lib/radio';
4
4
  import styles from './styles.module.scss';
5
5
 
6
6
  export const Radio = ({...radioProps }: RadioProps) => {
7
-
8
7
  return (
9
8
  <RadioAntd className={styles.radio} {...radioProps} />
10
9
  )