@afeefa/vue-app 0.0.46 → 0.0.50

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 (30) hide show
  1. package/.afeefa/package/release/version.txt +1 -1
  2. package/package.json +1 -1
  3. package/src/api-resources/ApiActions.js +4 -55
  4. package/src/components/AModal.vue +1 -2
  5. package/src/components/ASearchSelect.vue +5 -6
  6. package/src/components/ASelect.vue +10 -2
  7. package/src/components/list/{RouteFilterSource.js → CurrentRouteFilterSource.js} +2 -2
  8. package/src/components/list/FilterSourceType.js +7 -0
  9. package/src/components/list/ListFilterMixin.js +0 -3
  10. package/src/components/list/ListViewMixin.js +51 -41
  11. package/src/components/list/NextRouteFilterSource.js +15 -0
  12. package/src/components/list/filters/ListFilterSelect.vue +29 -24
  13. package/src/components/search-select/SearchSelectList.vue +4 -1
  14. package/src/plugins/api-resources/ApiResourcesPlugin.js +13 -0
  15. package/src/utils/props-helper.js +21 -0
  16. package/src-admin/bootstrap.js +2 -0
  17. package/src-admin/components/Splash.vue +8 -1
  18. package/src-admin/components/detail/DetailProperty.vue +5 -5
  19. package/src-admin/components/list/ListView.vue +6 -1
  20. package/src-admin/components/pages/CreatePage.vue +9 -12
  21. package/src-admin/components/pages/DetailPage.vue +5 -8
  22. package/src-admin/components/pages/EditPage.vue +10 -13
  23. package/src-admin/components/pages/ListPage.vue +8 -18
  24. package/src-admin/components/routes/DetailRoute.vue +7 -2
  25. package/src-admin/components/routes/EditRoute.vue +7 -2
  26. package/src-admin/components/routes/ListRoute.vue +20 -6
  27. package/src-admin/models/Model.js +10 -7
  28. package/src-admin/styles.scss +4 -2
  29. package/src/components/list/QuerySourceType.js +0 -4
  30. package/src/components/list/RouteParamsFilterSource.js +0 -18
@@ -1 +1 @@
1
- 0.0.46
1
+ 0.0.50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afeefa/vue-app",
3
- "version": "0.0.46",
3
+ "version": "0.0.50",
4
4
  "description": "",
5
5
  "author": "Afeefa Kollektiv <kollektiv@afeefa.de>",
6
6
  "license": "MIT",
@@ -1,8 +1,6 @@
1
- import { RouteParamsFilterSource } from '@a-vue/components/list/RouteParamsFilterSource'
2
1
  import { AlertEvent, DialogEvent, LoadingEvent, SaveEvent } from '@a-vue/events'
3
2
  import { eventBus } from '@a-vue/plugins/event-bus/EventBus'
4
3
  import { sleep } from '@a-vue/utils/timeout'
5
- import { RequestFilters } from '@afeefa/api-resources-client'
6
4
 
7
5
  export class GetAction {
8
6
  action = null
@@ -35,7 +33,7 @@ export class GetAction {
35
33
  eventBus.dispatch(new LoadingEvent(LoadingEvent.START_LOADING))
36
34
  }
37
35
 
38
- const result = await this.action.request()
36
+ const result = await this.action.createRequest()
39
37
  .params({
40
38
  id: this.id
41
39
  })
@@ -111,7 +109,7 @@ export class SaveAction {
111
109
 
112
110
  const startTime = Date.now()
113
111
 
114
- const result = await this.action.request()
112
+ const result = await this.action.createRequest()
115
113
  .params({
116
114
  id: this.id || undefined
117
115
  })
@@ -195,7 +193,7 @@ export class RemoveAction {
195
193
 
196
194
  const startTime = Date.now()
197
195
 
198
- const result = await this.action.request()
196
+ const result = await this.action.createRequest()
199
197
  .params({
200
198
  id: this.id
201
199
  })
@@ -230,11 +228,6 @@ export class RemoveAction {
230
228
 
231
229
  export class ListAction {
232
230
  request = null
233
- action = null
234
- fields = null
235
- scopes = {}
236
- filters = {}
237
- route = null
238
231
  events = true
239
232
 
240
233
  setRequest (request) {
@@ -242,31 +235,6 @@ export class ListAction {
242
235
  return this
243
236
  }
244
237
 
245
- setAction (action) {
246
- this.action = action
247
- return this
248
- }
249
-
250
- setFiltersForRoute (route) {
251
- this.route = route
252
- return this
253
- }
254
-
255
- setFields (fields) {
256
- this.fields = fields
257
- return this
258
- }
259
-
260
- setScopes (scopes) {
261
- this.scopes = scopes
262
- return this
263
- }
264
-
265
- setFilters (filters) {
266
- this.filters = filters
267
- return this
268
- }
269
-
270
238
  noEvents (noEvents) {
271
239
  this.events = noEvents === undefined ? false : !noEvents
272
240
  return this
@@ -277,26 +245,7 @@ export class ListAction {
277
245
  eventBus.dispatch(new LoadingEvent(LoadingEvent.START_LOADING))
278
246
  }
279
247
 
280
- let filters = this.filters
281
-
282
- if (this.route) {
283
- const querySource = new RouteParamsFilterSource(this.route)
284
- const requestFilters = this.action.createRequestFilters(null, querySource)
285
- const storedFilters = RequestFilters.fromHistory(this.route.path)
286
- if (storedFilters) {
287
- requestFilters.initFromUsed(storedFilters.serialize(), 1)
288
- }
289
- filters = requestFilters.serialize()
290
- }
291
-
292
- const request = this.request ||
293
- this.action
294
- .request()
295
- .scopes(this.scopes)
296
- .filters(filters)
297
- .fields(this.fields)
298
-
299
- const result = await request.send()
248
+ const result = await this.request.send()
300
249
 
301
250
  if (result.error) {
302
251
  if (this.events) {
@@ -64,7 +64,7 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentW
64
64
  }
65
65
 
66
66
  mounted () {
67
- // monkey patch onFousin to allow non vuetify-popups to receive focus
67
+ // monkey patch onFocusin to allow non vuetify-popups to receive focus
68
68
  const dialog = this.$refs.dialog
69
69
  const onFocusin = dialog.onFocusin
70
70
  dialog.onFocusin = e => {
@@ -127,7 +127,6 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentW
127
127
 
128
128
  createTransientAnchor () {
129
129
  let transientAnchorEl = document.querySelector('.' + this.transientAnchorClass)
130
- console.log('anchor', this.anchorPosition)
131
130
  if (!transientAnchorEl) {
132
131
  transientAnchorEl = document.createElement('div')
133
132
  transientAnchorEl.classList.add(this.transientAnchorClass)
@@ -46,11 +46,10 @@
46
46
  v-if="isOpen"
47
47
  v-bind="listConfig"
48
48
  :q="q"
49
- :noEvents="true"
50
- :noHistory="true"
49
+ :events="false"
50
+ :history="false"
51
51
  :filterSource="filterSource"
52
52
  :loadOnlyIfKeyword="_loadOnlyIfKeyword"
53
- :filters.sync="filters"
54
53
  :count.sync="count"
55
54
  :isLoading.sync="isLoading"
56
55
  >
@@ -76,7 +75,7 @@
76
75
  <div class="lastColumn" />
77
76
  </template>
78
77
 
79
- <template #not-found>
78
+ <template #not-found="{ filters }">
80
79
  <slot
81
80
  name="not-found"
82
81
  :filters="filters"
@@ -93,7 +92,7 @@ import { Component, Watch, Mixins } from 'vue-property-decorator'
93
92
  import { UsesPositionServiceMixin } from '../services/position/UsesPositionServiceMixin'
94
93
  import { PositionConfig } from '../services/PositionService'
95
94
  import { randomCssClass } from '../utils/random'
96
- import { QuerySourceType } from '@a-vue/components/list/QuerySourceType'
95
+ import { FilterSourceType } from '@a-vue/components/list/FilterSourceType'
97
96
  import SearchSelectFilters from './search-select/SearchSelectFilters'
98
97
  import SearchSelectList from './search-select/SearchSelectList'
99
98
  import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin'
@@ -109,7 +108,7 @@ export default class ASearchSelect extends Mixins(UsesPositionServiceMixin, Canc
109
108
  selectId = randomCssClass(10)
110
109
  isOpen = false
111
110
  items_ = []
112
- filterSource = QuerySourceType.OBJECT
111
+ filterSource = FilterSourceType.OBJECT
113
112
 
114
113
  isLoading = false
115
114
  filters = []
@@ -23,14 +23,22 @@ export default class ASelect extends Mixins(ComponentWidthMixin) {
23
23
  items_ = []
24
24
 
25
25
  mounted () {
26
- // monkey patch v-select setting 'undefined' on clearable
26
+ // monkey patch v-select to set default value on clear
27
+ const clearableCallback = this.select.clearableCallback
28
+ this.select.clearableCallback = () => {
29
+ this.select.isClear = true
30
+ clearableCallback()
31
+ this.select.isClear = false
32
+ }
33
+
27
34
  const setValue = this.select.setValue
28
35
  this.select.setValue = value => {
29
- if (!value && value !== false) { // if undefined alike
36
+ if (this.select.isClear) {
30
37
  value = this.defaultValue || null
31
38
  }
32
39
  setValue(value)
33
40
  }
41
+
34
42
  this.init()
35
43
  }
36
44
 
@@ -1,6 +1,6 @@
1
- import { BaseFilterSource } from '@afeefa/api-resources-client'
1
+ import { ListViewFilterSource } from '@afeefa/api-resources-client'
2
2
 
3
- export class RouteFilterSource extends BaseFilterSource {
3
+ export class CurrentRouteFilterSource extends ListViewFilterSource {
4
4
  router = null
5
5
 
6
6
  constructor (router) {
@@ -0,0 +1,7 @@
1
+ export class FilterSourceType {
2
+ // filters are synchronized with url's query string
3
+ static QUERY_STRING = 'query_string'
4
+
5
+ // filters are synchronized with plain history object
6
+ static OBJECT = 'object'
7
+ }
@@ -28,12 +28,9 @@ export class ListFilterMixin extends Vue {
28
28
 
29
29
  get filter () {
30
30
  const filter = this.filters[this._name]
31
-
32
-
33
31
  if (!filter) {
34
32
  console.warn(`Filter "${this._name}" not found`)
35
33
  }
36
-
37
34
  return filter
38
35
  }
39
36
 
@@ -1,23 +1,30 @@
1
1
  import { ListAction } from '@a-vue/api-resources/ApiActions'
2
+ import { propsWithDefaults } from '@a-vue/utils/props-helper'
3
+ import { ListViewModel } from '@afeefa/api-resources-client'
2
4
  import { Component, Vue, Watch } from 'vue-property-decorator'
3
5
 
4
- import { QuerySourceType } from './QuerySourceType'
5
- import { RouteFilterSource } from './RouteFilterSource'
6
+ import { CurrentRouteFilterSource } from './CurrentRouteFilterSource'
7
+ import { FilterSourceType } from './FilterSourceType'
6
8
 
7
9
  @Component({
8
- props: [
9
- 'models', 'meta', // if already loaded
10
- 'action', 'scopes', 'initialFilters', 'fields',
11
- 'noEvents', 'noHistory', 'filterHistoryKey', 'filterSource',
12
- 'loadOnlyIfKeyword'
13
- ]
10
+ ...propsWithDefaults([
11
+ 'models', 'meta', // given, if already loaded
12
+ 'listViewConfig',
13
+ 'filterHistoryKey',
14
+ 'loadOnlyIfKeyword',
15
+ {
16
+ filterSource: FilterSourceType.QUERY_STRING,
17
+ events: true,
18
+ history: true
19
+ }
20
+ ])
14
21
  })
15
22
  export class ListViewMixin extends Vue {
16
23
  LIST_VIEW = true
17
24
 
25
+ listViewModel = null
18
26
  models_ = []
19
27
  meta_ = {}
20
- requestFilters = null
21
28
  isLoading = false
22
29
 
23
30
  created () {
@@ -25,37 +32,41 @@ export class ListViewMixin extends Vue {
25
32
  }
26
33
 
27
34
  destroyed () {
28
- this.requestFilters.off('change', this.filtersChanged)
35
+ this.listViewModel.off('change', this.filtersChanged)
29
36
  }
30
37
 
31
38
  init () {
32
- if (this.models) {
33
- this.models_ = this.models
34
- this.meta_ = this.meta
35
- }
36
-
37
- if (this.requestFilters) {
38
- this.requestFilters.off('change', this.filtersChanged)
39
+ if (this.listViewModel) {
40
+ // this can happen only on HMR-reload
41
+ this.listViewModel.off('change', this.filtersChanged)
39
42
  }
40
43
 
41
- const historyKey = this.noHistory
42
- ? undefined
43
- : [this.$route.path, this.filterHistoryKey].filter(i => i).join('.')
44
- const querySource = this.filterSource === QuerySourceType.OBJECT ? undefined : new RouteFilterSource(this.$router)
45
- this.requestFilters = this.action.createRequestFilters(historyKey, querySource)
44
+ const historyKey = this.history
45
+ ? [this.$route.path, this.filterHistoryKey].filter(i => i).join('.')
46
+ : undefined
47
+ const filterSource = this.filterSource === FilterSourceType.QUERY_STRING
48
+ ? new CurrentRouteFilterSource(this.$router)
49
+ : undefined
46
50
 
47
- if (this.initialFilters) {
48
- console.log('set initial Filters: ', this.initialFilters)
49
- this.requestFilters.initFromUsed(this.initialFilters)
51
+ if (this.models) {
52
+ this.models_ = this.models
53
+ this.meta_ = this.meta
50
54
  }
51
55
 
52
- this.requestFilters.on('change', this.filtersChanged)
56
+ this.listViewModel = new ListViewModel(this.listViewConfig)
57
+ .filterSource(filterSource, !!filterSource)
58
+ .historyKey(historyKey, this.history)
59
+ .usedFilters(this.meta_.used_filters || null, this.meta_.count_search || 0)
60
+ .initFilters({
61
+ source: !!filterSource,
62
+ history: !!historyKey,
63
+ used: !!this.models
64
+ })
65
+ .on('change', this.filtersChanged) // listen to change
53
66
 
54
- this.$emit('update:filters', this.filters)
55
67
  this._filtersInitialized()
56
68
 
57
69
  if (this.models) {
58
- this.requestFilters.initFromUsed(this.meta_.used_filters, this.meta_.count_search)
59
70
  this.$emit('update:count', this.meta_.count_search)
60
71
  } else {
61
72
  this.load()
@@ -64,8 +75,8 @@ export class ListViewMixin extends Vue {
64
75
 
65
76
  @Watch('$route.query')
66
77
  routeQueryChanged () {
67
- if (this.filterSource !== QuerySourceType.ROUTE) {
68
- this.requestFilters.querySourceChanged()
78
+ if (this.filterSource === FilterSourceType.QUERY_STRING) {
79
+ this.listViewModel.filterSourceChanged()
69
80
  }
70
81
  }
71
82
 
@@ -79,16 +90,15 @@ export class ListViewMixin extends Vue {
79
90
  }
80
91
 
81
92
  filtersChanged () {
82
- console.log('filters changed')
83
93
  this.load()
84
94
  }
85
95
 
86
96
  resetFilters () {
87
- this.requestFilters.reset()
97
+ this.listViewModel.resetFilters()
88
98
  }
89
99
 
90
100
  get filters () {
91
- return this.requestFilters.getFilters()
101
+ return this.listViewModel.getFilters().getEntries()
92
102
  }
93
103
 
94
104
  get count () {
@@ -110,16 +120,16 @@ export class ListViewMixin extends Vue {
110
120
  this.isLoading = true
111
121
  this.$emit('update:isLoading', this.isLoading)
112
122
 
123
+ const request = this.listViewModel.getApiRequest()
124
+
113
125
  const {models, meta} = await new ListAction()
114
- .setAction(this.action)
115
- .setScopes(this.scopes)
116
- .setFilters(this.requestFilters.serialize())
117
- .setFields(this.fields)
118
- .noEvents(this.noEvents)
126
+ .setRequest(request)
127
+ .noEvents(!this.events)
119
128
  .load()
120
129
 
121
- if (!models) { // error, reset filters
122
- this.resetFilters()
130
+ if (!models) { // error happened
131
+ this.isLoading = false
132
+ this.$emit('update:isLoading', this.isLoading)
123
133
  return
124
134
  }
125
135
 
@@ -127,7 +137,7 @@ export class ListViewMixin extends Vue {
127
137
  this.meta_ = meta
128
138
 
129
139
  if (this.meta_.used_filters) {
130
- this.requestFilters.initFromUsed(this.meta_.used_filters, this.meta_.count_search)
140
+ this.listViewModel.initFromUsedFilters(this.meta_.used_filters, this.meta_.count_search)
131
141
  }
132
142
 
133
143
  this.isLoading = false
@@ -0,0 +1,15 @@
1
+ import { ListViewFilterSource } from '@afeefa/api-resources-client'
2
+
3
+ export class NextRouteFilterSource extends ListViewFilterSource {
4
+ route = null
5
+
6
+ constructor (route) {
7
+ super()
8
+
9
+ this.route = route
10
+ }
11
+
12
+ getQuery () {
13
+ return this.route.query
14
+ }
15
+ }
@@ -5,7 +5,8 @@
5
5
  :items="_items"
6
6
  itemText="itemTitle"
7
7
  itemValue="itemValue"
8
- :clearable="filter.value !== null"
8
+ :clearable="filter.value !== filter.defaultValue"
9
+ :defaultValue="filter.defaultValue"
9
10
  v-bind="$attrs"
10
11
  />
11
12
  </template>
@@ -16,16 +17,14 @@ import { Component, Mixins } from 'vue-property-decorator'
16
17
  import { ListFilterMixin } from '../ListFilterMixin'
17
18
  import { ListAction } from '@a-vue/api-resources/ApiActions'
18
19
 
19
- @Component({
20
- props: ['totalVisible']
21
- })
20
+ @Component
22
21
  export default class ListFilterSelect extends Mixins(ListFilterMixin) {
23
22
  items = null
24
23
 
25
24
  created () {
26
- if (this.hasOptionsRequest()) {
25
+ if (this.filter.hasOptionsRequest()) {
27
26
  this.items = this.loadRequestOptions()
28
- } else if (this.hasOptions()) {
27
+ } else if (this.filter.hasOptions()) {
29
28
  this.items = this.getOptions()
30
29
  }
31
30
  }
@@ -34,43 +33,49 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) {
34
33
  return this.$attrs.items || this.items || []
35
34
  }
36
35
 
37
- hasOptions () {
38
- return this.filter.options
36
+ get showNullOption () {
37
+ // either null is a selectable option, than it should be shown in the list
38
+ // or the default is null, so the list should offer a 'null' option for the unselected state
39
+ return this.filter.nullIsOption || this.filter.defaultValue === null
39
40
  }
40
41
 
41
- hasOptionsRequest () {
42
- return this.filter.request
42
+ createOptions () {
43
+ const options = []
44
+
45
+ if (this.showNullOption) {
46
+ options.push({
47
+ itemTitle: 'Alle',
48
+ itemValue: null
49
+ })
50
+ }
51
+
52
+ return options
43
53
  }
44
54
 
45
55
  async loadRequestOptions () {
46
56
  const {models} = await new ListAction()
47
- .setRequest(this.filter.request)
57
+ .setRequest(this.filter.createOptionsRequest())
48
58
  .noEvents()
49
59
  .load()
50
60
 
51
61
  return [
52
- {
53
- itemTitle: 'Alle',
54
- itemValue: null
55
- },
62
+ ...this.createOptions(),
56
63
  ...models.map(model => ({
57
64
  itemTitle: model.name,
58
65
  itemValue: model.id
59
66
  }))
60
-
61
67
  ]
62
68
  }
63
69
 
64
70
  getOptions () {
65
71
  return [
66
- {
67
- itemTitle: 'Alle',
68
- itemValue: null
69
- },
70
- ...this.filter.options.map(o => ({
71
- itemTitle: o ? 'Ja' : 'Nein',
72
- itemValue: o
73
- }))
72
+ ...this.createOptions(),
73
+ ...this.filter.options
74
+ .filter(o => o !== null) // null is already set in options (if any)
75
+ .map(o => ({
76
+ itemTitle: o ? 'Ja' : 'Nein',
77
+ itemValue: o
78
+ }))
74
79
  ]
75
80
  }
76
81
  }
@@ -26,7 +26,10 @@
26
26
  v-else-if="showNotFound"
27
27
  class="notFound"
28
28
  >
29
- <slot name="not-found">
29
+ <slot
30
+ name="not-found"
31
+ :filters="filters"
32
+ >
30
33
  Nichts gefunden.
31
34
  </slot>
32
35
  </div>
@@ -0,0 +1,13 @@
1
+ import { apiResources } from '@afeefa/api-resources-client'
2
+
3
+ class ApiResourcesPlugin {
4
+ install (Vue) {
5
+ Object.defineProperty(Vue.prototype, '$apiResources', {
6
+ get () {
7
+ return apiResources
8
+ }
9
+ })
10
+ }
11
+ }
12
+
13
+ export const apiResourcesPlugin = new ApiResourcesPlugin()
@@ -0,0 +1,21 @@
1
+ export function propsWithDefaults (props) {
2
+ const normalizedProps = {}
3
+
4
+ for (const prop of props) {
5
+ if (typeof prop === 'object') {
6
+ Object.keys(prop).forEach(subProp => {
7
+ if (typeof prop[subProp] === 'object') {
8
+ normalizedProps[subProp] = prop[subProp]
9
+ } else {
10
+ normalizedProps[subProp] = { default: prop[subProp] }
11
+ }
12
+ })
13
+ } else {
14
+ normalizedProps[prop] = null
15
+ }
16
+ }
17
+
18
+ return {
19
+ props: normalizedProps
20
+ }
21
+ }
@@ -1,6 +1,7 @@
1
1
  import './config/event-bus'
2
2
  import './config/components'
3
3
 
4
+ import { apiResourcesPlugin } from '@a-vue/plugins/api-resources/ApiResourcesPlugin'
4
5
  import { hasOptionsPlugin } from '@a-vue/plugins/has-options/HasOptionsPlugin'
5
6
  import { timeout } from '@a-vue/utils/timeout'
6
7
  import { apiResources } from '@afeefa/api-resources-client'
@@ -10,6 +11,7 @@ import { appConfig } from './config/AppConfig'
10
11
  import routeConfigPlugin from './config/routing'
11
12
  import vuetify from './config/vuetify'
12
13
 
14
+ Vue.use(apiResourcesPlugin)
13
15
  Vue.use(hasOptionsPlugin)
14
16
 
15
17
  Vue.config.productionTip = false
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-app>
2
+ <v-app class="splash">
3
3
  <v-container
4
4
  fill-height
5
5
  fluid
@@ -49,6 +49,13 @@ export default class Splash extends Vue {
49
49
 
50
50
 
51
51
  <style lang="scss" scoped>
52
+ .splash {
53
+ position: absolute;
54
+ top: 0;
55
+ left: 0;
56
+ width: 100vw;
57
+ }
58
+
52
59
  .logo {
53
60
  margin-bottom: 2rem;
54
61
  }
@@ -8,10 +8,10 @@
8
8
  >
9
9
  {{ _icon.icon }}
10
10
  </v-icon>
11
- <label :class="['label', {'label--withIcon': this._icon != null}]">{{ label }}</label>
11
+ <label :class="['label', {'label--withIcon': _icon != null}]">{{ label }}</label>
12
12
  </div>
13
13
 
14
- <div :class="['content', {'content--withIcon': this._icon != null}]">
14
+ <div :class="['content', {'content--withIcon': _icon != null}]">
15
15
  <a-row
16
16
  vertical
17
17
  gap="6"
@@ -37,8 +37,8 @@ export default class DetailProperty extends Vue {
37
37
  }
38
38
 
39
39
  if (this.iconModelType) {
40
- const model = apiResources.getModel(this.iconModelType)
41
- return model.icon
40
+ const ModelClass = apiResources.getModelClass(this.iconModelType)
41
+ return ModelClass.icon
42
42
  }
43
43
  }
44
44
  }
@@ -64,7 +64,7 @@ export default class DetailProperty extends Vue {
64
64
  @media (max-width: 900px), (orientation : portrait) {
65
65
  padding-left: 55px;
66
66
  &--withIcon {
67
- padding-left: 0;
67
+ padding-left: 0;
68
68
  }
69
69
  }
70
70
  }
@@ -31,6 +31,7 @@
31
31
  <slot
32
32
  name="model-table"
33
33
  :model="model"
34
+ :setFilter="setFilter"
34
35
  />
35
36
  </a-table-row>
36
37
  </a-table>
@@ -72,7 +73,7 @@ export default class ListView extends Mixins(ListViewMixin) {
72
73
 
73
74
  @Watch('isLoading')
74
75
  isLoadingChanged () {
75
- if (this.noEvents === undefined || !this.noEvents) {
76
+ if (this.events) {
76
77
  if (this.isLoading) {
77
78
  this.$events.dispatch(new LoadingEvent(LoadingEvent.START_LOADING))
78
79
  } else {
@@ -85,6 +86,10 @@ export default class ListView extends Mixins(ListViewMixin) {
85
86
  get _table () {
86
87
  return this.table !== false
87
88
  }
89
+
90
+ setFilter (name, value) {
91
+ this.filters[name].value = value
92
+ }
88
93
  }
89
94
  </script>
90
95
 
@@ -56,8 +56,8 @@ import { apiResources } from '@afeefa/api-resources-client'
56
56
  })
57
57
  export default class CreatePage extends Mixins(EditPageMixin) {
58
58
  created () {
59
- if (!this.$parent.constructor.getCreateConfig) {
60
- console.warn('<create-page> owner must provide a static getCreateConfig method.')
59
+ if (!this.$parent.constructor.createRouteConfig) {
60
+ console.warn('<create-page> owner must provide a static createRouteConfig method.')
61
61
  }
62
62
 
63
63
  this.reset()
@@ -65,11 +65,15 @@ export default class CreatePage extends Mixins(EditPageMixin) {
65
65
  }
66
66
 
67
67
  get editConfig () {
68
- return this.$parent.constructor.getCreateConfig(this.$route)
68
+ return this.$parent.constructor.createRouteConfig
69
69
  }
70
70
 
71
71
  get modelUpateAction () {
72
- return this.ModelClass.getAction(this.$routeDefinition, 'create')
72
+ return this.editConfig.createAction || this.ModelClass.getAction('create')
73
+ }
74
+
75
+ get _icon () {
76
+ return this.icon || this.modelToEdit.getIcon()
73
77
  }
74
78
 
75
79
  get _title () {
@@ -89,7 +93,7 @@ export default class CreatePage extends Mixins(EditPageMixin) {
89
93
  get _listLink () {
90
94
  if (this.listLink) {
91
95
  if (typeof this.listLink === 'function') {
92
- return this.listLink(this.$route.params)
96
+ return this.listLink()
93
97
  } else {
94
98
  return this.listLink
95
99
  }
@@ -97,13 +101,6 @@ export default class CreatePage extends Mixins(EditPageMixin) {
97
101
  return this.modelToEdit.getLink('list')
98
102
  }
99
103
 
100
- get _icon () {
101
- if (this.icon) {
102
- return this.icon
103
- }
104
- return this.modelToEdit.getIcon()
105
- }
106
-
107
104
  createModelToEdit () {
108
105
  if (this.createModel) {
109
106
  return this.createModel(this.fields)
@@ -55,7 +55,7 @@ import { Component, Vue, Watch } from 'vue-property-decorator'
55
55
  import { RemoveAction } from '@a-vue/api-resources/ApiActions'
56
56
 
57
57
  @Component({
58
- props: ['model', 'title', 'icon', 'removeAction', 'protectRemove', 'listLink']
58
+ props: ['model', 'title', 'icon', 'protectRemove', 'listLink']
59
59
  })
60
60
  export default class DetailPage extends Vue {
61
61
  $hasOptions = ['edit', 'remove', 'list']
@@ -64,7 +64,7 @@ export default class DetailPage extends Vue {
64
64
  removeConfirmed = null
65
65
 
66
66
  created () {
67
- if (!this.$parent.constructor.getDetailConfig) {
67
+ if (!this.$parent.constructor.detailRouteConfig) {
68
68
  console.warn('<detail-page> owner must provide a static getDetailConfig method.')
69
69
  }
70
70
  this.$emit('model', this.model)
@@ -80,7 +80,7 @@ export default class DetailPage extends Vue {
80
80
  }
81
81
 
82
82
  get detailConfig () {
83
- return this.$parent.constructor.getDetailConfig(this.$route)
83
+ return this.$parent.constructor.detailRouteConfig
84
84
  }
85
85
 
86
86
  get document () {
@@ -94,7 +94,7 @@ export default class DetailPage extends Vue {
94
94
  get _listLink () {
95
95
  if (this.listLink) {
96
96
  if (typeof this.listLink === 'function') {
97
- return this.listLink(this.$route.params)
97
+ return this.listLink()
98
98
  } else {
99
99
  return this.listLink
100
100
  }
@@ -110,10 +110,7 @@ export default class DetailPage extends Vue {
110
110
  }
111
111
 
112
112
  get _deleteAction () {
113
- if (this.removeAction) {
114
- return this.removeAction
115
- }
116
- return this.ModelClass.getAction(this.$routeDefinition, 'delete')
113
+ return this.detailConfig.removeAction || this.ModelClass.getAction('delete')
117
114
  }
118
115
 
119
116
  async remove () {
@@ -78,8 +78,8 @@ export default class EditPage extends Mixins(EditPageMixin) {
78
78
  model_ = null
79
79
 
80
80
  created () {
81
- if (!this.$parent.constructor.getEditConfig) {
82
- console.warn('<edit-page> owner must provide a static getEditConfig method.')
81
+ if (!this.$parent.constructor.editRouteConfig) {
82
+ console.warn('<edit-page> owner must provide a static editRouteConfig method.')
83
83
  }
84
84
 
85
85
  this.model_ = this.model
@@ -95,18 +95,22 @@ export default class EditPage extends Mixins(EditPageMixin) {
95
95
  }
96
96
 
97
97
  get editConfig () {
98
- return this.$parent.constructor.getEditConfig(this.$route)
98
+ return this.$parent.constructor.editRouteConfig
99
99
  }
100
100
 
101
101
  get modelUpateAction () {
102
- return this.ModelClass.getAction(this.$routeDefinition, 'update')
102
+ return this.editConfig.updateAction || this.ModelClass.getAction('update')
103
103
  }
104
104
 
105
105
  get _getAction () {
106
106
  if (this.getAction) {
107
107
  return this.getAction
108
108
  }
109
- return this.ModelClass.getAction(this.$routeDefinition, 'get')
109
+ return this.ModelClass.getAction('get')
110
+ }
111
+
112
+ get _icon () {
113
+ return this.icon || this.model.getIcon()
110
114
  }
111
115
 
112
116
  get _title () {
@@ -126,7 +130,7 @@ export default class EditPage extends Mixins(EditPageMixin) {
126
130
  get _listLink () {
127
131
  if (this.listLink) {
128
132
  if (typeof this.listLink === 'function') {
129
- return this.listLink(this.$route.params)
133
+ return this.listLink()
130
134
  } else {
131
135
  return this.listLink
132
136
  }
@@ -134,13 +138,6 @@ export default class EditPage extends Mixins(EditPageMixin) {
134
138
  return this.model.getLink('list')
135
139
  }
136
140
 
137
- get _icon () {
138
- if (this.icon) {
139
- return this.icon
140
- }
141
- return this.model.getIcon()
142
- }
143
-
144
141
  createModelToEdit () {
145
142
  return this.model_.cloneForEdit(this.fields)
146
143
  }
@@ -24,17 +24,21 @@ import { Component, Vue } from 'vue-property-decorator'
24
24
  import { apiResources } from '@afeefa/api-resources-client'
25
25
 
26
26
  @Component({
27
- props: ['title', 'newTitle', 'newLink', 'ModelClass']
27
+ props: ['icon', 'title', 'newTitle', 'newLink', 'Model']
28
28
  })
29
29
  export default class ListPage extends Vue {
30
30
  $hasOptions = ['add']
31
31
 
32
+ get _icon () {
33
+ return this.icon || this.Model.icon
34
+ }
35
+
32
36
  get _title () {
33
37
  if (this.title) {
34
38
  return this.title
35
39
  }
36
40
 
37
- const type = apiResources.getType(this.ModelClass.type)
41
+ const type = apiResources.getType(this.Model.type)
38
42
  return type.t('TITLE_PLURAL')
39
43
  }
40
44
 
@@ -43,26 +47,12 @@ export default class ListPage extends Vue {
43
47
  return this.newTitle
44
48
  }
45
49
 
46
- const type = apiResources.getType(this.ModelClass.type)
50
+ const type = apiResources.getType(this.Model.type)
47
51
  return type.t('TITLE_SINGULAR')
48
52
  }
49
53
 
50
- get _icon () {
51
- if (this.icon) {
52
- return this.icon
53
- }
54
- return this.ModelClass.icon
55
- }
56
-
57
54
  get _newLink () {
58
- if (this.newLink) {
59
- if (typeof this.newLink === 'function') {
60
- return this.newLink(this.$route.params)
61
- } else {
62
- return this.newLink
63
- }
64
- }
65
- return this.ModelClass.getLink('new')
55
+ return this.newLink || this.Model.getLink('new')
66
56
  }
67
57
  }
68
58
  </script>
@@ -18,8 +18,13 @@ Component.registerHooks([
18
18
  function load (route) {
19
19
  const routeDefinition = route.meta.routeDefinition
20
20
  const Component = routeDefinition.config.detail
21
- const detailConfig = Component.getDetailConfig(route)
22
- const action = detailConfig.action || detailConfig.ModelClass.getAction(routeDefinition, 'get')
21
+
22
+ if (!Component.detailRouteConfig) {
23
+ console.warn('A detail route component must implement a static detailRouteConfig property.')
24
+ }
25
+
26
+ const detailConfig = Component.detailRouteConfig
27
+ const action = detailConfig.action || detailConfig.ModelClass.getAction('get')
23
28
 
24
29
  return new GetAction()
25
30
  .setAction(action)
@@ -19,8 +19,13 @@ Component.registerHooks([
19
19
  function load (route) {
20
20
  const routeDefinition = route.meta.routeDefinition
21
21
  const Component = routeDefinition.config.edit
22
- const editConfig = Component.getEditConfig(route)
23
- const action = editConfig.getAction || editConfig.ModelClass.getAction(routeDefinition, 'get')
22
+
23
+ if (!Component.editRouteConfig) {
24
+ console.warn('An edit route component must implement a static editRouteConfig property.')
25
+ }
26
+
27
+ const editConfig = Component.editRouteConfig
28
+ const action = editConfig.getAction || editConfig.ModelClass.getAction('get')
24
29
 
25
30
  return new GetAction()
26
31
  .setAction(action)
@@ -10,6 +10,8 @@
10
10
  <script>
11
11
  import { Component, Vue, Watch } from 'vue-property-decorator'
12
12
  import { ListAction } from '@a-vue/api-resources/ApiActions'
13
+ import { NextRouteFilterSource } from '@a-vue/components/list/NextRouteFilterSource'
14
+ import { ListViewModel } from '@afeefa/api-resources-client'
13
15
 
14
16
  Component.registerHooks([
15
17
  'beforeRouteEnter',
@@ -28,14 +30,26 @@ let lastVm = null
28
30
  function load (route) {
29
31
  const routeDefinition = route.meta.routeDefinition
30
32
  const Component = routeDefinition.config.list
31
- const listConfig = Component.getListConfig(route)
32
- const action = listConfig.action || listConfig.ModelClass.getAction(routeDefinition, 'list')
33
+
34
+ if (!Component.listViewConfig) {
35
+ console.warn('A list route component must implement a static listViewConfig property.')
36
+ }
37
+
38
+ const request = new ListViewModel(Component.listViewConfig)
39
+ // read from next route query string, but do not push
40
+ // list component will be init with used_filters
41
+ .filterSource(new NextRouteFilterSource(route), false)
42
+ // read from history, but do not push
43
+ // list component will be init with used_filters
44
+ .historyKey(route.path, false)
45
+ .initFilters({
46
+ source: true,
47
+ history: true
48
+ })
49
+ .getApiRequest()
33
50
 
34
51
  return new ListAction()
35
- .setAction(action)
36
- .setFields(listConfig.fields)
37
- .setScopes(listConfig.scopes)
38
- .setFiltersForRoute(route)
52
+ .setRequest(request)
39
53
  .load()
40
54
  }
41
55
 
@@ -1,5 +1,6 @@
1
- import { Model as ApiResourcesModel } from '@afeefa/api-resources-client'
1
+ import { Model as ApiResourcesModel, apiResources } from '@afeefa/api-resources-client'
2
2
  import { mdiAlphaMCircle } from '@mdi/js'
3
+
3
4
  export class Model extends ApiResourcesModel {
4
5
  static resourceType = null
5
6
  static routeName = null
@@ -9,12 +10,14 @@ export class Model extends ApiResourcesModel {
9
10
  return (new this()).getLink(action)
10
11
  }
11
12
 
12
- static getAction (routeDefinition, action) {
13
+ static getAction (action) {
13
14
  if (this.resourceType) {
14
- const api = routeDefinition.config.api
15
- return api.getAction(this.resourceType, action)
15
+ return apiResources.getAction({
16
+ resource: this.resourceType,
17
+ action
18
+ })
16
19
  }
17
- console.warn('You can\'t get an action of a model without resourceType:', this.type)
20
+ console.warn('You can\'t get an action out of a model without resourceType:', this.type)
18
21
  return null
19
22
  }
20
23
 
@@ -32,8 +35,8 @@ export class Model extends ApiResourcesModel {
32
35
  }
33
36
  }
34
37
 
35
- getAction (routeDefinition, action) {
36
- return this.constructor.getAction(routeDefinition, action)
38
+ getAction (action) {
39
+ return this.constructor.getAction(action)
37
40
  }
38
41
 
39
42
  getIcon () {
@@ -21,14 +21,16 @@
21
21
  }
22
22
 
23
23
  .v-application {
24
- margin-bottom: 2rem;
25
-
26
24
  .text-strike {
27
25
  color: inherit;
28
26
  text-decoration: line-through;
29
27
  }
30
28
  }
31
29
 
30
+ .v-main {
31
+ margin-bottom: 2rem;
32
+ }
33
+
32
34
  .theme--light.v-btn.v-btn--has-bg {
33
35
  background-color: #E9E9E9;
34
36
  }
@@ -1,4 +0,0 @@
1
- export class QuerySourceType {
2
- static ROUTE = 'route'
3
- static OBJECT = 'object'
4
- }
@@ -1,18 +0,0 @@
1
- import { BaseFilterSource } from '@afeefa/api-resources-client'
2
-
3
- export class RouteParamsFilterSource extends BaseFilterSource {
4
- route = null
5
-
6
- constructor (route) {
7
- super()
8
-
9
- this.route = route
10
- }
11
-
12
- getQuery () {
13
- return this.route.query
14
- }
15
-
16
- push (_query) {
17
- }
18
- }