@farm-investimentos/front-mfe-components 12.1.3 → 12.2.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.
- package/dist/front-mfe-components.common.js +583 -5427
- package/dist/front-mfe-components.common.js.map +1 -1
- package/dist/front-mfe-components.css +1 -1
- package/dist/front-mfe-components.umd.js +583 -5427
- package/dist/front-mfe-components.umd.js.map +1 -1
- package/dist/front-mfe-components.umd.min.js +1 -1
- package/dist/front-mfe-components.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Chip/Chip.stories.js +6 -0
- package/src/components/Chip/Chip.vue +1 -1
- package/src/components/DialogHeader/DialogHeader.scss +3 -0
- package/src/components/DialogHeader/DialogHeader.stories.js +6 -0
- package/src/components/IdCaption/IdCaption.scss +15 -9
- package/src/components/IdCaption/IdCaption.stories.js +15 -10
- package/src/components/IdCaption/IdCaption.vue +16 -16
- package/src/components/ModalPromptUser/ModalPromptUser.stories.js +21 -0
- package/src/components/ModalPromptUser/ModalPromptUser.vue +1 -0
- package/src/components/SelectModalOptions/SelectModalOptions.scss +5 -0
- package/src/components/SelectModalOptions/SelectModalOptions.vue +81 -99
- package/src/components/SelectModalOptions/__tests__/SelectModalOptions.spec.js +1 -0
- package/src/components/TextArea/TextArea.scss +62 -0
- package/src/components/TextArea/TextArea.stories.js +136 -0
- package/src/components/TextArea/TextArea.vue +179 -0
- package/src/components/TextArea/__tests__/TextArea.spec.js +48 -0
- package/src/components/TextArea/index.ts +4 -0
- package/src/components/TextFieldV2/TextFieldV2.stories.js +0 -24
- package/src/components/Typography/Typography.scss +2 -5
- package/src/configurations/_mixins.scss +7 -0
- package/src/main.ts +1 -0
- package/src/examples/inputs/Password.stories.js +0 -42
- package/src/examples/inputs/Select.stories.js +0 -27
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { withDesign } from 'storybook-addon-designs';
|
|
2
|
+
import TextArea from './TextArea.vue';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Form/TextArea',
|
|
6
|
+
component: TextArea,
|
|
7
|
+
decorators: [withDesign],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component: `Text Area<br />
|
|
12
|
+
selector: <em>farm-textarea</em><br />
|
|
13
|
+
<span style="color: var(--farm-primary-base);">ready for use</span>
|
|
14
|
+
`,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
design: {
|
|
18
|
+
type: 'figma',
|
|
19
|
+
url: 'https://www.figma.com/file/jnZXo2e0nRJ3fVXl4Et8t4/%E2%9C%8D-Design-System-%7C-BACKUP-(10%2F10%2F2022)?node-id=2491%3A4487',
|
|
20
|
+
},
|
|
21
|
+
viewMode: 'docs',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const Primary = () => ({
|
|
26
|
+
data() {
|
|
27
|
+
return {
|
|
28
|
+
v: 'input text',
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
template: `<div style="width: 480px;">
|
|
32
|
+
<farm-textarea v-model="v" />
|
|
33
|
+
v-model: {{ v }}
|
|
34
|
+
</div>`,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const Rows = () => ({
|
|
38
|
+
data() {
|
|
39
|
+
return {
|
|
40
|
+
v: 'input text',
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
template: `<div style="width: 480px;">
|
|
44
|
+
<farm-textarea v-model="v" rows="10" />
|
|
45
|
+
<farm-textarea v-model="v" rows="3" />
|
|
46
|
+
</div>`,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export const Disabled = () => ({
|
|
50
|
+
data() {
|
|
51
|
+
return {
|
|
52
|
+
v: 'input text',
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
template: `<div style="width: 480px">
|
|
56
|
+
<farm-textarea v-model="v" disabled />
|
|
57
|
+
</div>`,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export const Readonly = () => ({
|
|
61
|
+
data() {
|
|
62
|
+
return {
|
|
63
|
+
v: 'input text',
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
template: `<div style="width: 480px">
|
|
67
|
+
<farm-textarea v-model="v" readonly />
|
|
68
|
+
</div>`,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
export const Validate = () => ({
|
|
72
|
+
data() {
|
|
73
|
+
return {
|
|
74
|
+
v1: 'input 1',
|
|
75
|
+
v2: '',
|
|
76
|
+
v4: '',
|
|
77
|
+
rules: {
|
|
78
|
+
required: value => !!value || 'Required field',
|
|
79
|
+
email: v =>
|
|
80
|
+
!v ||
|
|
81
|
+
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) ||
|
|
82
|
+
'Must be an e-mail',
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
template: `<div style="width: 480px">
|
|
87
|
+
<farm-label required>Required field</farm-label>
|
|
88
|
+
<farm-textarea v-model="v1" :rules="[rules.required]" />
|
|
89
|
+
|
|
90
|
+
<farm-label>E-mail</farm-label>
|
|
91
|
+
<farm-textarea v-model="v2" :rules="[rules.email]" />
|
|
92
|
+
|
|
93
|
+
<farm-label required>Required field with hint</farm-label>
|
|
94
|
+
<farm-textarea v-model="v4" :rules="[rules.required]" hint="hint text" />
|
|
95
|
+
|
|
96
|
+
</div>`,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export const HintText = () => ({
|
|
100
|
+
data() {
|
|
101
|
+
return {
|
|
102
|
+
v: 'input text',
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
template: `<div style="width: 480px; display: flex;">
|
|
106
|
+
<farm-textarea v-model="v" hint="Hint text" />
|
|
107
|
+
</div>`,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
export const Reset = () => ({
|
|
111
|
+
data() {
|
|
112
|
+
return {
|
|
113
|
+
v: 'input text',
|
|
114
|
+
rules: {
|
|
115
|
+
required: value => !!value || 'Required field',
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
methods: {
|
|
120
|
+
reset() {
|
|
121
|
+
this.$refs.input.reset();
|
|
122
|
+
this.$refs.inputValidatable.reset();
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
template: `<div style="width: 480px">
|
|
126
|
+
|
|
127
|
+
<farm-label>Not Required</farm-label>
|
|
128
|
+
<farm-textarea v-model="v" ref="input" />
|
|
129
|
+
|
|
130
|
+
<farm-label required>Required</farm-label>
|
|
131
|
+
<farm-textarea v-model="v" ref="inputValidatable" :rules="[rules.required]" />
|
|
132
|
+
|
|
133
|
+
<farm-btn @click="reset">reset</farm-btn>
|
|
134
|
+
|
|
135
|
+
</div>`,
|
|
136
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="farm-textarea"
|
|
4
|
+
:class="{
|
|
5
|
+
'farm-textarea': true,
|
|
6
|
+
'farm-textarea--validatable': rules.length > 0,
|
|
7
|
+
'farm-textarea--touched': isTouched,
|
|
8
|
+
'farm-textarea--blured': isBlured,
|
|
9
|
+
'farm-textarea--error': hasError,
|
|
10
|
+
'farm-textarea--disabled': disabled,
|
|
11
|
+
}"
|
|
12
|
+
>
|
|
13
|
+
<div :class="{
|
|
14
|
+
'farm-textarea--textarea': true,
|
|
15
|
+
}">
|
|
16
|
+
|
|
17
|
+
<textarea
|
|
18
|
+
v-bind="$attrs"
|
|
19
|
+
v-model="innerValue"
|
|
20
|
+
:rows="$props.rows"
|
|
21
|
+
:disabled="disabled"
|
|
22
|
+
:readonly="readonly"
|
|
23
|
+
@click="$emit('click')"
|
|
24
|
+
@keyup="onKeyUp"
|
|
25
|
+
@blur="onBlur"
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
<farm-caption v-if="showErrorText" color="error" variation="regular">
|
|
29
|
+
{{ errorBucket[0] }}
|
|
30
|
+
</farm-caption>
|
|
31
|
+
<farm-caption v-if="hint && !showErrorText" color="gray" variation="regular">
|
|
32
|
+
{{ hint }}
|
|
33
|
+
</farm-caption>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script lang="ts">
|
|
38
|
+
import Vue, { computed, onBeforeMount, PropType, ref, toRefs, watch } from 'vue';
|
|
39
|
+
import validateFormStateBuilder from '../../composition/validateFormStateBuilder';
|
|
40
|
+
import validateFormFieldBuilder from '../../composition/validateFormFieldBuilder';
|
|
41
|
+
import validateFormMethodBuilder from '../../composition/validateFormMethodBuilder';
|
|
42
|
+
import deepEqual from '../../composition/deepEqual';
|
|
43
|
+
|
|
44
|
+
export default Vue.extend({
|
|
45
|
+
name: 'farm-textarea',
|
|
46
|
+
inheritAttrs: true,
|
|
47
|
+
props: {
|
|
48
|
+
/**
|
|
49
|
+
* v-model binding
|
|
50
|
+
*/
|
|
51
|
+
value: { type: [String, Number], default: '' },
|
|
52
|
+
/**
|
|
53
|
+
* Show hint text
|
|
54
|
+
*/
|
|
55
|
+
hint: {
|
|
56
|
+
type: String,
|
|
57
|
+
default: null,
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* Disabled the input
|
|
61
|
+
*/
|
|
62
|
+
disabled: {
|
|
63
|
+
type: Boolean,
|
|
64
|
+
default: false,
|
|
65
|
+
},
|
|
66
|
+
/**
|
|
67
|
+
* Puts input in readonly state
|
|
68
|
+
*/
|
|
69
|
+
readonly: {
|
|
70
|
+
type: Boolean,
|
|
71
|
+
default: false,
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
errorMessage: String,
|
|
75
|
+
/**
|
|
76
|
+
* Array of rules used for validation
|
|
77
|
+
*/
|
|
78
|
+
rules: {
|
|
79
|
+
type: Array as PropType<Array<Function>>,
|
|
80
|
+
default: () => [],
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Textarea rows
|
|
84
|
+
*/
|
|
85
|
+
rows: {
|
|
86
|
+
default: 5,
|
|
87
|
+
type: [String, Number],
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
setup(props, { emit }) {
|
|
91
|
+
const { rules } = toRefs(props);
|
|
92
|
+
const innerValue = ref(props.value);
|
|
93
|
+
const isTouched = ref(false);
|
|
94
|
+
const isBlured = ref(false);
|
|
95
|
+
|
|
96
|
+
const { errorBucket, valid, validatable } = validateFormStateBuilder();
|
|
97
|
+
|
|
98
|
+
let fieldValidator = validateFormFieldBuilder(rules.value);
|
|
99
|
+
|
|
100
|
+
const hasError = computed(() => {
|
|
101
|
+
return errorBucket.value.length > 0;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const showErrorText = computed(() => hasError.value && isTouched.value);
|
|
105
|
+
|
|
106
|
+
watch(
|
|
107
|
+
() => props.value,
|
|
108
|
+
() => {
|
|
109
|
+
innerValue.value = props.value;
|
|
110
|
+
validate(innerValue.value);
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
watch(
|
|
115
|
+
() => innerValue.value,
|
|
116
|
+
() => {
|
|
117
|
+
emit('input', innerValue.value);
|
|
118
|
+
emit('change', innerValue.value);
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
watch(
|
|
123
|
+
() => props.rules,
|
|
124
|
+
(newVal, oldVal) => {
|
|
125
|
+
if (deepEqual(newVal, oldVal)) return;
|
|
126
|
+
fieldValidator = validateFormFieldBuilder(rules.value);
|
|
127
|
+
validate = validateFormMethodBuilder(errorBucket, valid, fieldValidator);
|
|
128
|
+
validate(innerValue.value);
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
onBeforeMount(() => {
|
|
133
|
+
validate(innerValue.value);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
let validate = validateFormMethodBuilder(errorBucket, valid, fieldValidator);
|
|
137
|
+
|
|
138
|
+
const onKeyUp = (event: Event) => {
|
|
139
|
+
isTouched.value = true;
|
|
140
|
+
emit('keyup', event);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const onBlur = (event: Event) => {
|
|
144
|
+
isBlured.value = true;
|
|
145
|
+
emit('blur', event);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const reset = () => {
|
|
149
|
+
innerValue.value = '';
|
|
150
|
+
isTouched.value = true;
|
|
151
|
+
emit('input', innerValue.value);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const makePristine = () => {
|
|
155
|
+
isTouched.value = false;
|
|
156
|
+
isBlured.value = false;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
innerValue,
|
|
161
|
+
errorBucket,
|
|
162
|
+
valid,
|
|
163
|
+
validatable,
|
|
164
|
+
hasError,
|
|
165
|
+
isTouched,
|
|
166
|
+
isBlured,
|
|
167
|
+
showErrorText,
|
|
168
|
+
validate,
|
|
169
|
+
onKeyUp,
|
|
170
|
+
onBlur,
|
|
171
|
+
reset,
|
|
172
|
+
makePristine,
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
</script>
|
|
177
|
+
<style lang="scss" scoped>
|
|
178
|
+
@import 'TextArea';
|
|
179
|
+
</style>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import TextArea from '../TextArea';
|
|
3
|
+
|
|
4
|
+
describe('TextArea component', () => {
|
|
5
|
+
let wrapper;
|
|
6
|
+
let component;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
wrapper = shallowMount(TextArea);
|
|
10
|
+
component = wrapper.vm;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('Created hook', () => {
|
|
14
|
+
expect(wrapper).toBeDefined();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('mount component', () => {
|
|
18
|
+
it('renders correctly', () => {
|
|
19
|
+
expect(wrapper.element).toMatchSnapshot();
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('methods', () => {
|
|
24
|
+
it('reset', () => {
|
|
25
|
+
component.reset();
|
|
26
|
+
expect(component.isTouched).toBeTruthy();
|
|
27
|
+
expect(component.innerValue).toEqual('');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('onKeyUp', () => {
|
|
31
|
+
component.onKeyUp();
|
|
32
|
+
expect(component.isTouched).toBeTruthy();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('onBlur', () => {
|
|
36
|
+
component.onBlur();
|
|
37
|
+
expect(component.isBlured).toBeTruthy();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('makePristine', () => {
|
|
41
|
+
component.isTouched = true;
|
|
42
|
+
component.isBlured = true;
|
|
43
|
+
component.makePristine();
|
|
44
|
+
expect(component.isTouched).toBeFalsy();
|
|
45
|
+
expect(component.isBlured).toBeFalsy();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -230,27 +230,3 @@ export const ToggleVisibility = () => ({
|
|
|
230
230
|
<farm-textfield-v2 v-model="v" :type="visible ? 'text' : 'password'" :icon="visible ? 'eye-off' : 'eye'" @onClickIcon="toggle" />
|
|
231
231
|
</div>`,
|
|
232
232
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
export const Compare = () => ({
|
|
236
|
-
data() {
|
|
237
|
-
return {
|
|
238
|
-
v1: '',
|
|
239
|
-
rules: {
|
|
240
|
-
required: value => !!value || 'Required field',
|
|
241
|
-
email: v =>
|
|
242
|
-
!v ||
|
|
243
|
-
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) ||
|
|
244
|
-
'Must be an e-mail',
|
|
245
|
-
},
|
|
246
|
-
};
|
|
247
|
-
},
|
|
248
|
-
template: `<div style="width: 480px">
|
|
249
|
-
|
|
250
|
-
<farm-label required>Required and e-mail</farm-label>
|
|
251
|
-
<farm-textfield-v2 v-model="v1" :rules="[rules.required, rules.email]" />
|
|
252
|
-
|
|
253
|
-
<farm-textfield v-model="v1" :rules="[rules.required, rules.email]" />
|
|
254
|
-
|
|
255
|
-
</div>`,
|
|
256
|
-
});
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
.farm-typography {
|
|
6
6
|
font-size: 16px;
|
|
7
|
+
color: var(--farm-text-primary);
|
|
7
8
|
|
|
8
9
|
&[bold] {
|
|
9
10
|
font-weight: bold;
|
|
@@ -25,16 +26,12 @@
|
|
|
25
26
|
margin-bottom: auto;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
color: var(--farm-text-primary);
|
|
29
|
-
|
|
30
29
|
&--lighten {
|
|
31
30
|
color: var(--farm-text-secondary);
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
&.farm-typography--ellipsis {
|
|
35
|
-
|
|
36
|
-
white-space: nowrap;
|
|
37
|
-
text-overflow: ellipsis;
|
|
34
|
+
@include ellipsis;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
@each $k in $theme-colors-list {
|
package/src/main.ts
CHANGED
|
@@ -87,6 +87,7 @@ export * from './components/RadioGroup';
|
|
|
87
87
|
export * from './components/Select';
|
|
88
88
|
export * from './components/Stepper';
|
|
89
89
|
export * from './components/Switcher';
|
|
90
|
+
export * from './components/TextArea';
|
|
90
91
|
export * from './components/TextField';
|
|
91
92
|
export * from './components/TextFieldV2';
|
|
92
93
|
export * from './components/Tooltip';
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
title: 'Form/Textfield/Examples/Password',
|
|
3
|
-
component: Password,
|
|
4
|
-
parameters: {
|
|
5
|
-
docs: {
|
|
6
|
-
description: {
|
|
7
|
-
component: `How to toggle password visivility.<br />
|
|
8
|
-
- Password visibility: hidden -> eye on icon (the click/tap will show the password)
|
|
9
|
-
- Password visibility: show -> eye off icon (the click/tap will hide the password)`,
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const Password = () => ({
|
|
16
|
-
data() {
|
|
17
|
-
return {
|
|
18
|
-
password: '',
|
|
19
|
-
showHidden: false,
|
|
20
|
-
};
|
|
21
|
-
},
|
|
22
|
-
template: `
|
|
23
|
-
<v-col cols="12" sm="12" md="4" class="v-col-fieldset-default pl-0">
|
|
24
|
-
<label>
|
|
25
|
-
Toggle visibility from password field
|
|
26
|
-
</label>
|
|
27
|
-
<v-text-field
|
|
28
|
-
color="secondary"
|
|
29
|
-
dense
|
|
30
|
-
outlined
|
|
31
|
-
v-model="password"
|
|
32
|
-
:append-icon="showHidden ? 'mdi-eye-off' : 'mdi-eye'"
|
|
33
|
-
:type="showHidden ? 'text' : 'password'"
|
|
34
|
-
@click:append="showHidden = !showHidden"
|
|
35
|
-
/>
|
|
36
|
-
</v-col>
|
|
37
|
-
`,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
Password.story = {
|
|
41
|
-
name: 'Básico',
|
|
42
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
title: 'Form/Select/Examples',
|
|
3
|
-
parameters: {
|
|
4
|
-
docs: {
|
|
5
|
-
description: {
|
|
6
|
-
component: `How to show select (v-select)<br />
|
|
7
|
-
`,
|
|
8
|
-
},
|
|
9
|
-
},
|
|
10
|
-
},
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const VSelect = () => ({
|
|
14
|
-
data() {
|
|
15
|
-
return {
|
|
16
|
-
items: ['SP', 'RJ', 'MG', 'RS', 'BA'],
|
|
17
|
-
};
|
|
18
|
-
},
|
|
19
|
-
template: `
|
|
20
|
-
<v-col cols="12" sm="6" md="2" class="v-col-fieldset-default pl-0">
|
|
21
|
-
<v-select dense outlined :items="items" />
|
|
22
|
-
</v-col>`,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
VSelect.story = {
|
|
26
|
-
name: 'Básico',
|
|
27
|
-
};
|