@bildvitta/quasar-ui-asteroid 3.16.0-beta.2 → 3.16.0-beta.4

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@bildvitta/quasar-ui-asteroid",
3
3
  "description": "Asteroid",
4
- "version": "3.16.0-beta.2",
4
+ "version": "3.16.0-beta.4",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -43,6 +43,7 @@
43
43
  "sass": "^1.62.1"
44
44
  },
45
45
  "dependencies": {
46
+ "@bildvitta/composables": "^1.0.0-beta.7",
46
47
  "@bildvitta/store-adapter": "^1.0.0",
47
48
  "autonumeric": "^4.9.0",
48
49
  "axios": "^1.4.0",
@@ -3,7 +3,7 @@
3
3
  <qas-btn-dropdown v-bind="btnDropdownProps">
4
4
  <q-list data-cy="actions-menu-list">
5
5
  <slot v-for="(item, key) in formattedList.dropdownList" :item="item" :name="key">
6
- <q-item v-bind="getItemProps(item)" :key="key" clickable data-cy="actions-menu-list-item" @click="setClickHandler(item)">
6
+ <q-item v-bind="getItemProps(item)" :key="key" active-class="primary" clickable data-cy="actions-menu-list-item" @click="setClickHandler(item)">
7
7
  <q-item-section avatar>
8
8
  <q-icon :name="item.icon" />
9
9
  </q-item-section>
@@ -240,10 +240,11 @@ const { showTooltip, tooltipLabels } = useTooltips({ formattedList, fullList, pr
240
240
 
241
241
  // functions
242
242
  function getItemProps (item) {
243
- const { disable, props: itemProps } = item
243
+ const { disable, props: itemProps, to } = item
244
244
 
245
245
  return {
246
246
  disable,
247
+ to,
247
248
  ...itemProps
248
249
  }
249
250
  }
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="justify-between no-wrap q-col-gutter-x-md q-mb-xl row text-body1 text-grey-8" :class="containerClass">
2
+ <div class="justify-between no-wrap q-col-gutter-x-md row text-body1 text-grey-8" :class="containerClass">
3
3
  <div :class="leftClass">
4
4
  <slot name="left">
5
5
  {{ props.text }}
@@ -18,6 +18,8 @@
18
18
 
19
19
  <script setup>
20
20
  import { FlexAlign } from '../../enums/Align'
21
+ import { Spacing } from '../../enums/Spacing'
22
+ import { gutterValidator } from '../../helpers/private/gutter-validator'
21
23
 
22
24
  import { computed, useSlots } from 'vue'
23
25
 
@@ -43,13 +45,20 @@ const props = defineProps({
43
45
  text: {
44
46
  type: String,
45
47
  default: ''
48
+ },
49
+
50
+ spacing: {
51
+ default: Spacing.Xl,
52
+ type: String,
53
+ validator: gutterValidator
46
54
  }
47
55
  })
48
56
 
49
57
  const slots = useSlots()
50
58
 
51
59
  // computed
52
- const containerClass = computed(() => `items-${props.alignColumns}`)
60
+ const containerClass = computed(() => `items-${props.alignColumns} q-mb-${props.spacing}`)
61
+
53
62
  const leftClass = computed(() => hasRightSide.value ? 'col-9 col-md-9 col-sm-8' : 'col-12')
54
63
 
55
64
  const hasDefaultButton = computed(() => !!Object.keys(props.buttonProps).length)
@@ -24,6 +24,12 @@ props:
24
24
  desc: Descrição da seção a esquerda.
25
25
  type: String
26
26
 
27
+ spacing:
28
+ desc: Espaçamento entre o componente e o conteúdo abaixo.
29
+ default: xl
30
+ type: String
31
+ examples: [none, xs, sm, md, lg, xl, '2xl', '3xl', '4xl', '5xl']
32
+
27
33
  slots:
28
34
  left:
29
35
  desc: slot para acessar seção da esquerda (descrição).
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <div :class="mx_componentClass">
3
- <header v-if="mx_hasHeaderSlot">
2
+ <div :class="componentClass">
3
+ <header v-if="hasHeaderSlot">
4
4
  <slot name="header" />
5
5
  </header>
6
6
 
@@ -8,117 +8,160 @@
8
8
  <slot />
9
9
  </template>
10
10
 
11
- <qas-empty-result-text v-else-if="!mx_isFetching" class="q-my-xl" />
11
+ <qas-empty-result-text v-else-if="!viewState.fetching" class="q-my-xl" />
12
12
 
13
- <footer v-if="mx_hasFooterSlot">
13
+ <footer v-if="hasFooterSlot">
14
14
  <slot name="footer" />
15
15
  </footer>
16
16
 
17
- <q-inner-loading :showing="mx_isFetching">
17
+ <q-inner-loading :showing="viewState.fetching">
18
18
  <q-spinner color="grey" size="3em" />
19
19
  </q-inner-loading>
20
20
  </div>
21
21
  </template>
22
22
 
23
- <script>
24
- import viewMixin from '../../mixins/view'
23
+ <script setup>
24
+ import useView, { baseProps, baseEmits } from '../../composables/private/use-view'
25
25
 
26
- import { markRaw } from 'vue'
27
- import { getGetter, getAction } from '@bildvitta/store-adapter'
28
26
  import debug from 'debug'
27
+ import { decamelize } from 'humps'
28
+ import { markRaw, computed, watch, inject } from 'vue'
29
+ import { useRoute } from 'vue-router'
29
30
 
30
31
  const log = debug('asteroid-ui:qas-single-view')
31
32
 
32
- export default {
33
- name: 'QasSingleView',
33
+ defineOptions({ name: 'QasSingleView' })
34
34
 
35
- mixins: [viewMixin],
35
+ const props = defineProps({
36
+ ...baseProps,
36
37
 
37
- props: {
38
- customId: {
39
- default: '',
40
- type: [Number, String]
41
- },
42
-
43
- result: {
44
- default: () => ({}),
45
- type: Object
46
- }
38
+ customId: {
39
+ default: '',
40
+ type: [Number, String]
47
41
  },
48
42
 
49
- emits: [
50
- 'update:result',
51
- 'fetch-success',
52
- 'fetch-error'
53
- ],
43
+ result: {
44
+ default: () => ({}),
45
+ type: Object
46
+ }
47
+ })
54
48
 
55
- computed: {
56
- hasResult () {
57
- return !!this.resultModel
58
- },
49
+ const emit = defineEmits([
50
+ ...baseEmits,
59
51
 
60
- id () {
61
- return this.customId || this.$route.params.id
62
- },
52
+ 'update:result',
53
+ 'fetch-success',
54
+ 'fetch-error'
55
+ ])
63
56
 
64
- resultModel () {
65
- return getGetter.call(this, { entity: this.entity, key: 'byId' })(this.id) || {}
66
- }
67
- },
57
+ const slots = defineSlots()
68
58
 
69
- watch: {
70
- $route (to, from) {
71
- if (to.name === from.name) {
72
- this.mx_fetchHandler({ id: this.id, url: this.url }, this.fetchSingle)
73
- }
74
- },
59
+ // globals
60
+ const axios = inject('axios')
61
+ const qas = inject('qas')
75
62
 
76
- resultModel (value) {
77
- this.$emit('update:result', markRaw({ ...value }))
78
- }
79
- },
63
+ // composables
64
+ const route = useRoute()
80
65
 
81
- created () {
82
- this.mx_fetchHandler({ id: this.id, url: this.url }, this.fetchSingle)
83
- },
66
+ const {
67
+ // state
68
+ viewState,
69
+
70
+ // computed
71
+ componentClass,
72
+ hasHeaderSlot,
73
+ hasFooterSlot,
74
+
75
+ // functions
76
+ errorHandler,
77
+ fetchHandler,
78
+ setErrors,
79
+ setFields,
80
+ setMetadata,
81
+ setResult,
82
+ updateModels
83
+ } = useView({ emit, props, slots, mode: 'single' })
84
+
85
+ // computed
86
+ const id = computed(() => props.customId || route.params.id)
87
+
88
+ const resultModel = computed(() => {
89
+ if (props.useStore) return qas.getGetter({ entity: props.entity, key: 'byId' })(id.value) || {}
90
+
91
+ return viewState.value.result || {}
92
+ })
93
+
94
+ const hasResult = computed(() => !!resultModel.value)
95
+
96
+ // watch
97
+ watch(() => route, (to, from) => {
98
+ if (to.name === from.name) {
99
+ fetchHandler({ id: id.value, url: props.url }, fetchSingle)
100
+ }
101
+ })
102
+
103
+ watch(() => resultModel.value, value => emit('update:result', markRaw({ ...value })))
104
+
105
+ // created
106
+ fetchHandler({ id: id.value, url: props.url }, fetchSingle)
107
+
108
+ // functions
109
+ async function fetchSingle (externalPayload = {}) {
110
+ viewState.value.fetching = true
111
+
112
+ try {
113
+ const payload = { id: id.value, url: props.url, ...externalPayload }
114
+
115
+ const response = await getAction(payload)
84
116
 
85
- methods: {
86
- async fetchSingle (externalPayload = {}) {
87
- this.mx_isFetching = true
117
+ const { errors, fields, metadata, result } = response.data
88
118
 
89
- try {
90
- const payload = { id: this.id, url: this.url, ...externalPayload }
119
+ setErrors(errors)
120
+ setFields(fields)
121
+ setMetadata(metadata)
91
122
 
92
- const response = await getAction.call(this, {
93
- entity: this.entity,
94
- key: 'fetchSingle',
95
- payload
96
- })
123
+ /**
124
+ * result só precisa ser adicionado ao estado se não estiver usando store,
125
+ * pois com a store é usado o getter.
126
+ */
127
+ !props.useStore && setResult(result)
97
128
 
98
- const { errors, fields, metadata } = response.data
129
+ updateModels({
130
+ errors: viewState.value.errors,
131
+ fields: viewState.value.fields,
132
+ metadata: viewState.value.metadata,
99
133
 
100
- this.mx_setErrors(errors)
101
- this.mx_setFields(fields)
102
- this.mx_setMetadata(metadata)
134
+ ...(!props.useStore && { result: viewState.value.result })
135
+ })
103
136
 
104
- this.mx_updateModels({
105
- errors: this.mx_errors,
106
- fields: this.mx_fields,
107
- metadata: this.mx_metadata
108
- })
137
+ emit('fetch-success', response)
109
138
 
110
- this.$emit('fetch-success', response)
139
+ log(`[${props.entity}]:fetchSingle:success`, response)
140
+ } catch (error) {
141
+ errorHandler(error)
142
+ emit('fetch-error', error)
111
143
 
112
- log(`[${this.entity}]:fetchSingle:success`, response)
113
- } catch (error) {
114
- this.mx_fetchError(error)
115
- this.$emit('fetch-error', error)
144
+ log(`[${props.entity}]:fetchSingle:error`, error)
145
+ } finally {
146
+ viewState.value.fetching = false
147
+ }
148
+ }
116
149
 
117
- log(`[${this.entity}]:fetchSingle:error`, error)
118
- } finally {
119
- this.mx_isFetching = false
120
- }
121
- }
150
+ function getAction (payload) {
151
+ if (props.useStore) {
152
+ return qas.getAction({
153
+ entity: props.entity,
154
+ key: 'fetchSingle',
155
+ payload
156
+ })
122
157
  }
158
+
159
+ const { id: unusedID, url: unusedURL, ...externalPayload } = payload
160
+
161
+ const decamelizedEntity = decamelize(props.entity, { separator: '-' })
162
+
163
+ const url = props.url || `${decamelizedEntity}/${id.value}`
164
+
165
+ return axios.get(url, { ...externalPayload })
123
166
  }
124
167
  </script>
@@ -62,6 +62,11 @@ props:
62
62
  default: true
63
63
  type: Boolean
64
64
 
65
+ use-route:
66
+ desc: Controla se o componente usará internamente uma store do vuex/pinia ou se vai ser utilizado o axios diretamente.
67
+ default: true
68
+ type: Boolean
69
+
65
70
  slots:
66
71
  default:
67
72
  desc: 'Slot para ter o conteúdo principal (dentro do main).'
@@ -0,0 +1,2 @@
1
+ export { default as useView } from './use-view'
2
+ export { default as useGenerator } from './use-generator'
@@ -0,0 +1,180 @@
1
+ import { NotifyError } from '../../plugins'
2
+ import { camelizeFieldsName } from '../../helpers'
3
+
4
+ import { useView as useViewComposable } from '@bildvitta/composables'
5
+ import { ref, computed, watch, markRaw } from 'vue'
6
+ import { useRouter } from 'vue-router'
7
+
8
+ export const baseProps = {
9
+ beforeFetch: {
10
+ default: null,
11
+ type: Function
12
+ },
13
+
14
+ errors: {
15
+ default: () => ({}),
16
+ type: Object
17
+ },
18
+
19
+ entity: {
20
+ type: String,
21
+ required: ''
22
+ },
23
+
24
+ fetching: {
25
+ type: Boolean
26
+ },
27
+
28
+ fields: {
29
+ default: () => ({}),
30
+ type: Object
31
+ },
32
+
33
+ metadata: {
34
+ default: () => ({}),
35
+ type: Object
36
+ },
37
+
38
+ url: {
39
+ default: '',
40
+ type: String
41
+ },
42
+
43
+ useBoundary: {
44
+ default: true,
45
+ type: Boolean
46
+ },
47
+
48
+ useStore: {
49
+ default: true,
50
+ type: Boolean
51
+ }
52
+ }
53
+
54
+ export const baseEmits = [
55
+ 'update:fields',
56
+ 'update:errors',
57
+ 'update:metadata',
58
+ 'update:fetching'
59
+ ]
60
+
61
+ /**
62
+ * @param {{
63
+ * mode: import('@bildvitta/composables').ViewModeTypes,
64
+ * props: baseProps,
65
+ * emit: (baseEmits: string) => void,
66
+ * slots: import('vue').Slots
67
+ * }} config
68
+ */
69
+ export default function useView (config) {
70
+ const {
71
+ emit,
72
+ mode,
73
+ slots,
74
+ props
75
+ } = config
76
+
77
+ // composables
78
+ const router = useRouter()
79
+ const { viewState } = useViewComposable({ mode })
80
+
81
+ // refs
82
+ const cancelBeforeFetch = ref(false)
83
+
84
+ // constants
85
+ const fetchErrorMessage = 'Ops… Não conseguimos acessar as informações. Por favor, tente novamente em alguns minutos.'
86
+
87
+ // computed
88
+ const componentClass = computed(() => props.useBoundary && 'container spaced')
89
+ const hasFooterSlot = computed(() => !!slots.footer)
90
+ const hasHeaderSlot = computed(() => !!slots.header)
91
+
92
+ // watch
93
+ watch(() => viewState.value.fetching, value => emit('update:fetching', value))
94
+
95
+ // functions
96
+ function errorHandler (error) {
97
+ const { response } = error
98
+
99
+ const status = response?.status
100
+
101
+ const redirect = status >= 500
102
+ ? 'ServerError'
103
+ : ({ 403: 'Forbidden', 404: 'NotFound' })[status]
104
+
105
+ /**
106
+ * caso exista um desses status será redirecionado sem aparecer o "notify"
107
+ */
108
+ if (redirect) {
109
+ router.replace({ name: redirect })
110
+
111
+ return
112
+ }
113
+
114
+ NotifyError(fetchErrorMessage)
115
+ }
116
+
117
+ function setErrors (errors = {}) {
118
+ viewState.value.errors = markRaw(errors)
119
+ }
120
+
121
+ function setFields (fields = {}) {
122
+ const camelizedFields = camelizeFieldsName(fields)
123
+
124
+ viewState.value.fields = markRaw(camelizedFields)
125
+ }
126
+
127
+ function setMetadata (metadata = {}) {
128
+ viewState.value.metadata = markRaw(metadata)
129
+ }
130
+
131
+ function setResult (result = {}) {
132
+ viewState.value.result = result
133
+ }
134
+
135
+ function updateModels (models) {
136
+ for (const key in models) {
137
+ if (!models[key]) continue
138
+
139
+ emit(`update:${key}`, models[key])
140
+ }
141
+ }
142
+
143
+ function fetchHandler (payload, resolve) {
144
+ const hasBeforeFetch = typeof props.beforeFetch === 'function'
145
+
146
+ if (hasBeforeFetch && !cancelBeforeFetch.value) {
147
+ return props.beforeFetch({
148
+ payload,
149
+ resolve: payload => resolve(payload),
150
+ done: () => {
151
+ cancelBeforeFetch.value = true
152
+ }
153
+ })
154
+ }
155
+
156
+ resolve()
157
+ }
158
+
159
+ return {
160
+ // state
161
+ viewState,
162
+
163
+ // constants
164
+ fetchErrorMessage,
165
+
166
+ // computed
167
+ componentClass,
168
+ hasFooterSlot,
169
+ hasHeaderSlot,
170
+
171
+ // functions
172
+ errorHandler,
173
+ fetchHandler,
174
+ setErrors,
175
+ setFields,
176
+ setResult,
177
+ setMetadata,
178
+ updateModels
179
+ }
180
+ }
package/src/vue-plugin.js CHANGED
@@ -72,7 +72,7 @@ import QasWhatsappLink from './components/whatsapp-link/QasWhatsappLink.vue'
72
72
 
73
73
  import { Notify, Loading, Quasar, Dialog as QuasarDialog } from 'quasar'
74
74
 
75
- import { getAction } from '@bildvitta/store-adapter'
75
+ import { getAction, getGetter } from '@bildvitta/store-adapter'
76
76
 
77
77
  // Plugins
78
78
  import {
@@ -176,7 +176,8 @@ async function install (app) {
176
176
  app.provide('qas', {
177
177
  delete: params => Delete.call(app.config.globalProperties, params),
178
178
 
179
- getAction: params => getAction.call(app.config.globalProperties, params)
179
+ getAction: params => getAction.call(app.config.globalProperties, params),
180
+ getGetter: params => getGetter.call(app.config.globalProperties, params)
180
181
  })
181
182
 
182
183
  app.directive(Test.name, Test)