@afeefa/vue-app 0.0.54 → 0.0.55

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. package/.afeefa/package/release/version.txt +1 -1
  2. package/package.json +1 -1
  3. package/src/components/AContextMenu.vue +1 -1
  4. package/src/components/AContextMenuItem.vue +1 -1
  5. package/src/components/ARichTextArea.vue +121 -0
  6. package/src/components/ASearchSelect.vue +12 -12
  7. package/src/components/form/EditForm.vue +1 -5
  8. package/src/components/form/FormFieldMixin.js +2 -3
  9. package/src/components/form/fields/FormFieldRichTextArea.vue +14 -0
  10. package/src/components/index.js +2 -0
  11. package/src/components/list/ListViewMixin.js +4 -0
  12. package/src/components/search-select/SearchSelectList.vue +5 -1
  13. package/src/index.js +2 -0
  14. package/src/plugins/api-resources/ApiResourcesPlugin.js +12 -0
  15. package/src/styles/forms.scss +8 -0
  16. package/src/styles/vue-app.scss +1 -0
  17. package/src-admin/bootstrap.js +2 -5
  18. package/src-admin/components/controls/SearchSelectFormField.vue +223 -0
  19. package/src-admin/components/detail/DetailProperty.vue +1 -3
  20. package/src-admin/components/list/ListColumnHeader.vue +4 -3
  21. package/src-admin/components/list/ListView.vue +11 -9
  22. package/src-admin/components/pages/CreatePage.vue +1 -2
  23. package/src-admin/components/pages/DetailPage.vue +3 -3
  24. package/src-admin/components/pages/EditPage.vue +4 -5
  25. package/src-admin/components/pages/ListPage.vue +2 -3
  26. package/src-admin/components/routes/DetailRoute.vue +3 -3
  27. package/src-admin/components/routes/EditRoute.vue +3 -3
  28. package/src-admin/components/routes/ListRoute.vue +3 -3
  29. package/src-admin/config/vuetify.js +3 -1
  30. package/src-admin/models/Model.js +13 -0
  31. package/src-admin/models/ModelAdminConfig.js +20 -0
  32. package/src-components/AMdiIcon.vue +14 -0
@@ -1 +1 @@
1
- 0.0.54
1
+ 0.0.55
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afeefa/vue-app",
3
- "version": "0.0.54",
3
+ "version": "0.0.55",
4
4
  "description": "",
5
5
  "author": "Afeefa Kollektiv <kollektiv@afeefa.de>",
6
6
  "license": "MIT",
@@ -140,7 +140,7 @@ export default class AContextMenu extends Mixins(UsesPositionServiceMixin) {
140
140
  .popUpContent {
141
141
  min-height: 2.2rem;
142
142
  position: absolute;
143
- z-index: 200;
143
+ z-index: 400;
144
144
  display: block;
145
145
  background-color: white;
146
146
  padding: 0.5rem;
@@ -29,7 +29,7 @@ export default class AContextMenuItem extends Vue {
29
29
  this.contextMenu.close()
30
30
  if (this.to) {
31
31
  this.$router.push(this.to)
32
- .catch(() => null) // prevent duplicated navigation
32
+ .catch(() => null) // prevent duplicated navigation warning
33
33
  } else {
34
34
  this.$emit('click')
35
35
  }
@@ -0,0 +1,121 @@
1
+ <template>
2
+ <div :class="['a-rich-text-editor a-text-input', {'a-text-input-focused': focus}]">
3
+ <div v-if="editor">
4
+ <button
5
+ type="button"
6
+ :class="{ 'is-active': editor.isActive('bold') }"
7
+ @click="editor.chain().focus().toggleBold().run()"
8
+ >
9
+ bold
10
+ <a-mdi-icon />
11
+ </button>
12
+ <button
13
+ type="button"
14
+ :class="{ 'is-active': editor.isActive('italic') }"
15
+ @click="editor.chain().focus().toggleItalic().run()"
16
+ >
17
+ italic
18
+ </button>
19
+ <button
20
+ type="button"
21
+ :class="{ 'is-active': editor.isActive('strike') }"
22
+ @click="editor.chain().focus().toggleStrike().run()"
23
+ >
24
+ strike
25
+ </button>
26
+ </div>
27
+
28
+ <editor-content
29
+ :editor="editor"
30
+ :class="['a-rich-text-editor', {focus}]"
31
+ />
32
+ </div>
33
+ </template>
34
+
35
+
36
+ <script>
37
+ import { Component, Vue, Watch } from '@a-vue'
38
+ import { Editor, EditorContent } from '@tiptap/vue-2'
39
+ import StarterKit from '@tiptap/starter-kit'
40
+
41
+ @Component({
42
+ props: ['value', 'validator'],
43
+ components: {
44
+ EditorContent
45
+ }
46
+ })
47
+ export default class ARichTextArea extends Vue {
48
+ editor = null
49
+ internalValue = null
50
+ focus = false
51
+
52
+ created () {
53
+ this.internalValue = this.value
54
+ }
55
+
56
+ mounted () {
57
+ if (this.validator) {
58
+ this.$refs.input.validate(true)
59
+ }
60
+
61
+ this.editor = new Editor({
62
+ content: '<p>I’m running Tiptap with Vue.js. 🎉</p>',
63
+ extensions: [
64
+ StarterKit
65
+ ],
66
+ onUpdate: () => {
67
+ this.$emit('input', this.editor.getHTML())
68
+ },
69
+ onFocus: ({ editor, event }) => {
70
+ this.focus = true
71
+ },
72
+ onBlur: ({ editor, event }) => {
73
+ this.focus = false
74
+ }
75
+ })
76
+
77
+ this.editor.commands.setContent(this.internalValue, false)
78
+ }
79
+
80
+ beforeDestroy () {
81
+ this.editor.destroy()
82
+ }
83
+
84
+ @Watch('value')
85
+ valueChanged () {
86
+ this.internalValue = this.value
87
+
88
+ const isSame = this.editor.getHTML() === this.internalValue
89
+ if (!isSame) {
90
+ this.editor.commands.setContent(this.internalValue, false)
91
+ }
92
+ }
93
+
94
+ get validationRules () {
95
+ const label = this.$attrs.label
96
+ return (this.validator && this.validator.getRules(label)) || []
97
+ }
98
+
99
+ get counter () {
100
+ if (!this.validator) {
101
+ return false
102
+ }
103
+ return this.validator.getParams().max || false
104
+ }
105
+ }
106
+ </script>
107
+
108
+
109
+ <style lang="scss" scoped>
110
+ .v-input:not(.v-input--is-focused) ::v-deep .v-counter {
111
+ display: none;
112
+ }
113
+
114
+ .a-rich-text-editor {
115
+ ::v-deep .ProseMirror {
116
+ &-focused {
117
+ outline: none;
118
+ }
119
+ }
120
+ }
121
+ </style>
@@ -16,6 +16,7 @@
16
16
  <div
17
17
  v-if="isOpen"
18
18
  class="controls"
19
+ :style="cwm_widthStyle"
19
20
  >
20
21
  <div class="background elevation-6" />
21
22
 
@@ -54,6 +55,7 @@
54
55
  :filters.sync="filters"
55
56
  :count.sync="count"
56
57
  :isLoading.sync="isLoading"
58
+ :style="cwm_widthStyle"
57
59
  >
58
60
  <template #header>
59
61
  <div />
@@ -62,19 +64,11 @@
62
64
  </template>
63
65
 
64
66
  <template #row="{ model }">
65
- <v-icon
66
- :color="model.getIcon().color"
67
- size="1.5rem"
68
- v-text="model.getIcon().icon"
69
- />
70
-
71
67
  <slot
72
68
  name="row"
73
69
  :model="model"
74
70
  :on="{ click: selectHandler(model) }"
75
71
  />
76
-
77
- <div class="lastColumn" />
78
72
  </template>
79
73
 
80
74
  <template #not-found="{ filters }">
@@ -98,6 +92,7 @@ import { FilterSourceType } from '@a-vue/components/list/FilterSourceType'
98
92
  import SearchSelectFilters from './search-select/SearchSelectFilters'
99
93
  import SearchSelectList from './search-select/SearchSelectList'
100
94
  import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin'
95
+ import { ComponentWidthMixin } from './mixins/ComponentWidthMixin'
101
96
 
102
97
  @Component({
103
98
  props: [
@@ -115,7 +110,7 @@ import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin'
115
110
  SearchSelectList
116
111
  }
117
112
  })
118
- export default class ASearchSelect extends Mixins(UsesPositionServiceMixin, CancelOnEscMixin) {
113
+ export default class ASearchSelect extends Mixins(ComponentWidthMixin, UsesPositionServiceMixin, CancelOnEscMixin) {
119
114
  selectId = randomCssClass(10)
120
115
  isOpen = false
121
116
  items_ = []
@@ -318,20 +313,25 @@ export default class ASearchSelect extends Mixins(UsesPositionServiceMixin, Canc
318
313
  }
319
314
 
320
315
  .controls {
321
- min-width: 400px;
316
+ width: 400px;
322
317
  position: absolute;
323
318
  z-index: 300;
324
319
  display: block;
325
- padding: 0 0.5rem;
320
+ padding: 0 .5rem;
321
+
322
+ ::v-deep .a-row {
323
+ overflow: unset;
324
+ }
326
325
  }
327
326
 
328
327
  .searchSelectList {
329
- min-width: 400px;
328
+ width: 400px;
330
329
  position: absolute;
331
330
  z-index: 301;
332
331
 
333
332
  max-height: 40vh;
334
333
  overflow-y: auto;
334
+ overflow-x: hidden;
335
335
  overscroll-behavior: contain;
336
336
 
337
337
  ::v-deep .a-table-row {
@@ -8,6 +8,7 @@
8
8
  <slot
9
9
  :changed="changed"
10
10
  :valid="valid"
11
+ :model="model"
11
12
  />
12
13
  </v-form>
13
14
  </template>
@@ -15,7 +16,6 @@
15
16
 
16
17
  <script>
17
18
  import { Component, Vue, Watch } from '@a-vue'
18
- import { apiResources } from '@afeefa/api-resources-client'
19
19
 
20
20
  @Component({
21
21
  props: ['model']
@@ -52,9 +52,5 @@ export default class EditForm extends Vue {
52
52
  changedChanged () {
53
53
  this.$emit('update:changed', this.changed)
54
54
  }
55
-
56
- get type () {
57
- return apiResources.getType(this.model.type)
58
- }
59
55
  }
60
56
  </script>
@@ -1,6 +1,5 @@
1
- import { ListAction } from '@a-vue/api-resources/ApiActions'
2
- import { apiResources } from '@afeefa/api-resources-client'
3
1
  import { Component, Vue } from '@a-vue'
2
+ import { ListAction } from '@a-vue/api-resources/ApiActions'
4
3
 
5
4
  @Component({
6
5
  props: ['name', 'label']
@@ -17,7 +16,7 @@ export class FormFieldMixin extends Vue {
17
16
  }
18
17
 
19
18
  get modelType () {
20
- return apiResources.getType(this.model.type)
19
+ return this.$apiResources.getType(this.model.type)
21
20
  }
22
21
 
23
22
  get field () {
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <a-rich-text-area
3
+ v-model="model[name]"
4
+ />
5
+ </template>
6
+
7
+ <script>
8
+ import { Component, Mixins } from '@a-vue'
9
+ import { FormFieldMixin } from '../FormFieldMixin'
10
+
11
+ @Component
12
+ export default class FormFieldRichTextArea extends Mixins(FormFieldMixin) {
13
+ }
14
+ </script>
@@ -5,6 +5,7 @@ import EditModal from './form/EditModal'
5
5
  import FormFieldCheckbox from './form/fields/FormFieldCheckbox'
6
6
  import FormFieldDate from './form/fields/FormFieldDate'
7
7
  import FormFieldRadioGroup from './form/fields/FormFieldRadioGroup'
8
+ import FormFieldRichTextArea from './form/fields/FormFieldRichTextArea'
8
9
  import FormFieldSearchSelect from './form/fields/FormFieldSearchSelect'
9
10
  import FormFieldSelect from './form/fields/FormFieldSelect'
10
11
  import FormFieldSelect2 from './form/fields/FormFieldSelect2'
@@ -19,6 +20,7 @@ Vue.component('EditForm', EditForm)
19
20
  Vue.component('EditModal', EditModal)
20
21
  Vue.component('FormFieldText', FormFieldText)
21
22
  Vue.component('FormFieldTextArea', FormFieldTextArea)
23
+ Vue.component('FormFieldRichTextArea', FormFieldRichTextArea)
22
24
  Vue.component('FormFieldRadioGroup', FormFieldRadioGroup)
23
25
  Vue.component('FormFieldCheckbox', FormFieldCheckbox)
24
26
  Vue.component('FormFieldDate', FormFieldDate)
@@ -92,6 +92,10 @@ export class ListViewMixin extends Vue {
92
92
  this.load()
93
93
  }
94
94
 
95
+ reload () {
96
+ this.load()
97
+ }
98
+
95
99
  resetFilters () {
96
100
  this.listViewModel.resetFilters()
97
101
  }
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="searchSelectList">
2
+ <div :class="['searchSelectList', {isLoading}]">
3
3
  <template v-if="models_.length">
4
4
  <a-table v-bind="$attrs">
5
5
  <a-table-header
@@ -74,6 +74,10 @@ export default class SearchSelectList extends Mixins(ListViewMixin) {
74
74
 
75
75
 
76
76
  <style scoped lang="scss">
77
+ .isLoading {
78
+ opacity: .6;
79
+ }
80
+
77
81
  .notFound {
78
82
  padding: 0 .5rem .3rem;
79
83
  }
package/src/index.js CHANGED
@@ -1,2 +1,4 @@
1
+ import './styles/vue-app.scss'
2
+
1
3
  export { Mixins, Vue, Watch } from 'vue-property-decorator'
2
4
  export { Component } from '@a-vue/components/vue/Component'
@@ -1,6 +1,18 @@
1
1
  import { apiResources } from '@afeefa/api-resources-client'
2
2
 
3
3
  class ApiResourcesPlugin {
4
+ apiResources = apiResources
5
+
6
+ register (models, apis) {
7
+ apiResources
8
+ .registerModels(models)
9
+ .registerApis(apis)
10
+ }
11
+
12
+ schemasLoaded () {
13
+ return apiResources.schemasLoaded()
14
+ }
15
+
4
16
  install (Vue) {
5
17
  Object.defineProperty(Vue.prototype, '$apiResources', {
6
18
  get () {
@@ -0,0 +1,8 @@
1
+ .a-text-input {
2
+ border: 1px solid #CCCCCC;
3
+ padding: .5rem;
4
+
5
+ &-focused {
6
+ outline: 1px solid #9999FF;
7
+ }
8
+ }
@@ -0,0 +1 @@
1
+ @import "forms";
@@ -4,7 +4,6 @@ import './config/components'
4
4
  import { apiResourcesPlugin } from '@a-vue/plugins/api-resources/ApiResourcesPlugin'
5
5
  import { hasOptionsPlugin } from '@a-vue/plugins/has-options/HasOptionsPlugin'
6
6
  import { timeout } from '@a-vue/utils/timeout'
7
- import { apiResources } from '@afeefa/api-resources-client'
8
7
  import Vue from 'vue'
9
8
 
10
9
  import { appConfig } from './config/AppConfig'
@@ -17,9 +16,7 @@ Vue.use(hasOptionsPlugin)
17
16
  Vue.config.productionTip = false
18
17
 
19
18
  export async function bootstrap ({ apis, models, routing, authService, app, components }) {
20
- apiResources
21
- .registerModels(models)
22
- .registerApis(apis)
19
+ apiResourcesPlugin.register(models, apis)
23
20
 
24
21
  appConfig.authService = authService
25
22
  appConfig.app = app
@@ -36,7 +33,7 @@ export async function bootstrap ({ apis, models, routing, authService, app, comp
36
33
 
37
34
  routing(routeConfigPlugin)
38
35
  const router = await routeConfigPlugin.getRouter()
39
- await apiResources.schemasLoaded()
36
+ await apiResourcesPlugin.schemasLoaded()
40
37
 
41
38
  if (authService) {
42
39
  authService.initApp(router)
@@ -0,0 +1,223 @@
1
+ <template>
2
+ <div>
3
+ <a-table
4
+ v-if="selectedItems.length"
5
+ :style="selectedWidthStyle"
6
+ >
7
+ <a-table-header
8
+ v-if="hasHeader"
9
+ small
10
+ >
11
+ <div
12
+ v-for="(column, index) in selectedColumns"
13
+ :key="index"
14
+ >
15
+ {{ column.header }}
16
+ </div>
17
+ </a-table-header>
18
+
19
+ <a-table-row
20
+ v-for="(item, index) in selectedItems"
21
+ :key="item.id"
22
+ :selected="isClicked(item)"
23
+ >
24
+ <template v-for="(column, index2) in selectedColumns">
25
+ <v-icon
26
+ v-if="column.icon"
27
+ :key="'icon' + index2"
28
+ :color="column.icon.color"
29
+ class="selectedIcon"
30
+ size="1.5rem"
31
+ v-text="column.icon.icon"
32
+ />
33
+
34
+ <div
35
+ v-else
36
+ :key="'text' + index2"
37
+ class="selectedContent"
38
+ >
39
+ {{ column.text(item) }}
40
+ </div>
41
+ </template>
42
+
43
+ <div>
44
+ <a-context-menu
45
+ :ref="'editItem' + index"
46
+ @open="clickedItem = item"
47
+ @close="clickedItem = null"
48
+ >
49
+ <a-context-menu-item @click="removeItem(item, index)">
50
+ <v-icon>$trashCanIcon</v-icon>
51
+ Löschen
52
+ </a-context-menu-item>
53
+ </a-context-menu>
54
+ </div>
55
+ </a-table-row>
56
+ </a-table>
57
+
58
+ <div v-else>
59
+ {{ isEmptyText }}
60
+ </div>
61
+
62
+ <a-search-select
63
+ :listViewConfig="selectableConfig.listViewConfig"
64
+ :loadOnlyIfKeyword="false"
65
+ :selectedItems="selectableSelectedItems"
66
+ :width="selectableWidth"
67
+ @select="addItem"
68
+ >
69
+ <template #activator>
70
+ <a-icon-button
71
+ icon="$plusIcon"
72
+ :text="selectableConfig.addButtonTitle || 'Hinzufügen'"
73
+ class="mt-4"
74
+ />
75
+ </template>
76
+
77
+ <template #filters>
78
+ <list-filter-row>
79
+ <list-filter-search
80
+ :focus="true"
81
+ maxWidth="100%"
82
+ label="Suche Berater:in"
83
+ />
84
+
85
+ <list-filter-page
86
+ :has="{page_size: false, page_number: true}"
87
+ :totalVisible="0"
88
+ />
89
+ </list-filter-row>
90
+ </template>
91
+
92
+ <template #row="{ model, on }">
93
+ <template
94
+ v-for="(column, index) in selectableColumns"
95
+ v-on="on"
96
+ >
97
+ <v-icon
98
+ v-if="column.icon"
99
+ :key="'icon' + index"
100
+ :color="column.icon.color"
101
+ class="selectableIcon"
102
+ size="1.5rem"
103
+ v-on="on"
104
+ v-text="column.icon.icon"
105
+ />
106
+
107
+ <div
108
+ v-else
109
+ :key="'text' + index"
110
+ class="selectableContent"
111
+ v-on="on"
112
+ >
113
+ {{ column.text(model) }}
114
+ </div>
115
+ </template>
116
+ </template>
117
+ </a-search-select>
118
+ </div>
119
+ </template>
120
+
121
+
122
+ <script>
123
+ import { Component, Vue } from '@a-vue'
124
+
125
+ @Component({
126
+ props: [
127
+ 'value',
128
+ 'selectedConfig',
129
+ 'selectedWidth',
130
+ 'selectableConfig',
131
+ 'selectableWidth'
132
+ ]
133
+ })
134
+ export default class SearchSelectFormField extends Vue {
135
+ clickedItem = null
136
+
137
+ get hasHeader () {
138
+ return this.selectedColumns.some(c => !!c.header)
139
+ }
140
+
141
+ get selectedItems () {
142
+ return this.value
143
+ }
144
+
145
+ get selectableSelectedItems () {
146
+ return this.value.map(this.selectedConfig.selectedToSelectableItem)
147
+ }
148
+
149
+ get isEmptyText () {
150
+ return this.selectedConfig.isEmptyText
151
+ }
152
+
153
+ isClicked (selectedItem) {
154
+ return selectedItem === this.clickedItem
155
+ }
156
+
157
+ get selectedColumns () {
158
+ return this.selectedConfig.columns
159
+ }
160
+
161
+ get selectableColumns () {
162
+ return this.selectableConfig.columns
163
+ }
164
+
165
+ addItem (selectableItem) {
166
+ const selectedItem = this.selectedConfig.newSelectedItem(selectableItem)
167
+ const selectedItems = [...this.value, selectedItem]
168
+ if (this.$listeners.add) {
169
+ this.$emit('add', selectedItems)
170
+ } else {
171
+ this.$emit('input', selectedItems)
172
+ }
173
+ }
174
+
175
+ async removeItem (selectedItem, rowIndex) {
176
+ const removeDialog = this.selectedConfig.removeDialog(selectedItem)
177
+ const dialog = {
178
+ anchor: this.$refs['editItem' + rowIndex], // is array [component]
179
+ ...removeDialog,
180
+ yesButton: 'Löschen',
181
+ yesColor: 'red white--text'
182
+ }
183
+
184
+ const selectedItems = this.value.filter(i => i !== selectedItem)
185
+ if (this.$listeners.remove) {
186
+ this.$emit('remove', selectedItems, dialog)
187
+ } else {
188
+ this.$emit('input', selectedItems)
189
+ }
190
+ }
191
+
192
+ get selectedWidthStyle () {
193
+ if (!this.selectedWidth) {
194
+ return ''
195
+ }
196
+
197
+ let width = this.selectedWidth
198
+ if (!isNaN(width)) {
199
+ width = width + 'px'
200
+ }
201
+ return `width: ${width};`
202
+ }
203
+ }
204
+ </script>
205
+
206
+
207
+ <style scoped lang="scss">
208
+ .selectedIcon::v-deep {
209
+ padding-right: .3rem !important;
210
+ }
211
+
212
+ .selectableIcon::v-deep {
213
+ padding-right: 2rem !important;
214
+ }
215
+
216
+ .selectedContent::v-deep {
217
+ width: 100%;
218
+ }
219
+
220
+ .selectableContent::v-deep {
221
+ width: 100%;
222
+ }
223
+ </style>
@@ -25,7 +25,6 @@
25
25
 
26
26
  <script>
27
27
  import { Component, Vue } from '@a-vue'
28
- import { apiResources } from '@afeefa/api-resources-client'
29
28
 
30
29
  @Component({
31
30
  props: ['icon', 'iconModelType', 'label']
@@ -37,7 +36,7 @@ export default class DetailProperty extends Vue {
37
36
  }
38
37
 
39
38
  if (this.iconModelType) {
40
- const ModelClass = apiResources.getModelClass(this.iconModelType)
39
+ const ModelClass = this.$apiResources.getModelClass(this.iconModelType)
41
40
  return ModelClass.icon
42
41
  }
43
42
  }
@@ -77,6 +76,5 @@ export default class DetailProperty extends Vue {
77
76
  padding-left: 55px;
78
77
  }
79
78
  }
80
-
81
79
  }
82
80
  </style>
@@ -4,9 +4,10 @@
4
4
  :class="['content', {order}]"
5
5
  @click="toggleSort"
6
6
  >
7
- <div :class="['text', {active}]">
8
- {{ text }}
9
- </div>
7
+ <div
8
+ :class="['text', {active}]"
9
+ v-html="text"
10
+ />
10
11
 
11
12
  <svg
12
13
  v-if="order"
@@ -9,7 +9,15 @@
9
9
  </div>
10
10
 
11
11
  <template v-if="models_.length">
12
- <template v-if="_table">
12
+ <template v-if="$scopedSlots.models">
13
+ <slot
14
+ name="models"
15
+ :models="models_"
16
+ :setFilter="setFilter"
17
+ />
18
+ </template>
19
+
20
+ <template v-else-if="$scopedSlots['model-table']">
13
21
  <a-table>
14
22
  <a-table-header>
15
23
  <div v-if="$has.icon" />
@@ -37,7 +45,7 @@
37
45
  </a-table>
38
46
  </template>
39
47
 
40
- <template v-else>
48
+ <template v-else-if="$scopedSlots.model">
41
49
  <div
42
50
  v-for="model in models_"
43
51
  :key="model.id"
@@ -65,9 +73,7 @@ import { Component, Watch, Mixins } from '@a-vue'
65
73
  import { ListViewMixin } from '@a-vue/components/list/ListViewMixin'
66
74
  import { LoadingEvent } from '@a-vue/events'
67
75
 
68
- @Component({
69
- props: ['table']
70
- })
76
+ @Component
71
77
  export default class ListView extends Mixins(ListViewMixin) {
72
78
  $hasOptions = ['icon']
73
79
 
@@ -83,10 +89,6 @@ export default class ListView extends Mixins(ListViewMixin) {
83
89
  this.$emit('update:isLoading', this.isLoading)
84
90
  }
85
91
 
86
- get _table () {
87
- return this.table !== false
88
- }
89
-
90
92
  setFilter (name, value) {
91
93
  this.filters[name].value = value
92
94
  }
@@ -49,7 +49,6 @@
49
49
  <script>
50
50
  import { Component, Mixins } from '@a-vue'
51
51
  import { EditPageMixin } from './EditPageMixin'
52
- import { apiResources } from '@afeefa/api-resources-client'
53
52
 
54
53
  @Component({
55
54
  props: ['icon', 'title', 'createModel', 'listLink']
@@ -86,7 +85,7 @@ export default class CreatePage extends Mixins(EditPageMixin) {
86
85
  return this.title
87
86
  }
88
87
 
89
- const type = apiResources.getType(this.ModelClass.type)
88
+ const type = this.$apiResources.getType(this.ModelClass.type)
90
89
  return type.t('TITLE_NEW')
91
90
  }
92
91
 
@@ -64,8 +64,8 @@ export default class DetailPage extends Vue {
64
64
  removeConfirmed = null
65
65
 
66
66
  created () {
67
- if (!this.$parent.constructor.detailRouteConfig) {
68
- console.warn('<detail-page> owner must provide a static getDetailConfig method.')
67
+ if (!this.$parent.constructor.getDetailRouteConfig) {
68
+ console.warn('<detail-page> owner must provide a static getDetailRouteConfig method.')
69
69
  }
70
70
  this.$emit('model', this.model)
71
71
  }
@@ -80,7 +80,7 @@ export default class DetailPage extends Vue {
80
80
  }
81
81
 
82
82
  get detailConfig () {
83
- return this.$parent.constructor.detailRouteConfig
83
+ return this.$parent.constructor.getDetailRouteConfig(this.$route)
84
84
  }
85
85
 
86
86
  get document () {
@@ -66,7 +66,6 @@
66
66
 
67
67
  <script>
68
68
  import { Component, Mixins, Watch } from '@a-vue'
69
- import { apiResources } from '@afeefa/api-resources-client'
70
69
  import { EditPageMixin } from './EditPageMixin'
71
70
 
72
71
  @Component({
@@ -78,8 +77,8 @@ export default class EditPage extends Mixins(EditPageMixin) {
78
77
  model_ = null
79
78
 
80
79
  created () {
81
- if (!this.$parent.constructor.editRouteConfig) {
82
- console.warn('<edit-page> owner must provide a static editRouteConfig method.')
80
+ if (!this.$parent.constructor.getEditRouteConfig) {
81
+ console.warn('<edit-page> owner must provide a static getEditRouteConfig method.')
83
82
  }
84
83
 
85
84
  this.model_ = this.model
@@ -95,7 +94,7 @@ export default class EditPage extends Mixins(EditPageMixin) {
95
94
  }
96
95
 
97
96
  get editConfig () {
98
- return this.$parent.constructor.editRouteConfig
97
+ return this.$parent.constructor.getEditRouteConfig(this.$route)
99
98
  }
100
99
 
101
100
  get modelUpateAction () {
@@ -123,7 +122,7 @@ export default class EditPage extends Mixins(EditPageMixin) {
123
122
  return this.title
124
123
  }
125
124
 
126
- const type = apiResources.getType(this.ModelClass.type)
125
+ const type = this.$apiResources.getType(this.ModelClass.type)
127
126
  return type.t('TITLE_EMPTY')
128
127
  }
129
128
 
@@ -21,7 +21,6 @@
21
21
 
22
22
  <script>
23
23
  import { Component, Vue } from '@a-vue'
24
- import { apiResources } from '@afeefa/api-resources-client'
25
24
 
26
25
  @Component({
27
26
  props: ['icon', 'title', 'newTitle', 'newLink', 'Model']
@@ -38,7 +37,7 @@ export default class ListPage extends Vue {
38
37
  return this.title
39
38
  }
40
39
 
41
- const type = apiResources.getType(this.Model.type)
40
+ const type = this.$apiResources.getType(this.Model.type)
42
41
  return type.t('TITLE_PLURAL')
43
42
  }
44
43
 
@@ -47,7 +46,7 @@ export default class ListPage extends Vue {
47
46
  return this.newTitle
48
47
  }
49
48
 
50
- const type = apiResources.getType(this.Model.type)
49
+ const type = this.$apiResources.getType(this.Model.type)
51
50
  return type.t('TITLE_SINGULAR')
52
51
  }
53
52
 
@@ -19,11 +19,11 @@ function load (route) {
19
19
  const routeDefinition = route.meta.routeDefinition
20
20
  const Component = routeDefinition.config.detail
21
21
 
22
- if (!Component.detailRouteConfig) {
23
- console.warn('A detail route component must implement a static detailRouteConfig property.')
22
+ if (!Component.getDetailRouteConfig) {
23
+ console.warn('A detail route component must implement a static getDetailRouteConfig property.')
24
24
  }
25
25
 
26
- const detailConfig = Component.detailRouteConfig
26
+ const detailConfig = Component.getDetailRouteConfig(route)
27
27
  const action = detailConfig.action || detailConfig.ModelClass.getAction('get')
28
28
 
29
29
  return new GetAction()
@@ -20,11 +20,11 @@ function load (route) {
20
20
  const routeDefinition = route.meta.routeDefinition
21
21
  const Component = routeDefinition.config.edit
22
22
 
23
- if (!Component.editRouteConfig) {
24
- console.warn('An edit route component must implement a static editRouteConfig property.')
23
+ if (!Component.getEditRouteConfig) {
24
+ console.warn('An edit route component must implement a static getEditRouteConfig property.')
25
25
  }
26
26
 
27
- const editConfig = Component.editRouteConfig
27
+ const editConfig = Component.getEditRouteConfig(route)
28
28
  const action = editConfig.getAction || editConfig.ModelClass.getAction('get')
29
29
 
30
30
  return new GetAction()
@@ -31,11 +31,11 @@ function load (route) {
31
31
  const routeDefinition = route.meta.routeDefinition
32
32
  const Component = routeDefinition.config.list
33
33
 
34
- if (!Component.listViewConfig) {
35
- console.warn('A list route component must implement a static listViewConfig property.')
34
+ if (!Component.getListRouteConfig) {
35
+ console.warn('A list route component must implement a static getListRouteConfig property.')
36
36
  }
37
37
 
38
- const request = new ListViewModel(Component.listViewConfig)
38
+ const request = new ListViewModel(Component.getListRouteConfig(route))
39
39
  // read from next route query string, but do not push
40
40
  // list component will be init with used_filters
41
41
  .filterSource(new NextRouteFilterSource(route), false)
@@ -6,6 +6,7 @@ import {
6
6
  mdiDelete,
7
7
  mdiDotsHorizontal,
8
8
  mdiDotsVertical,
9
+ mdiLock,
9
10
  mdiLogoutVariant,
10
11
  mdiMagnify,
11
12
  mdiPencil,
@@ -32,7 +33,8 @@ export default new Vuetify({
32
33
  pencilIcon: mdiPencil,
33
34
  trashCanIcon: mdiDelete,
34
35
  calendarIcon: mdiCalendar,
35
- searchIcon: mdiMagnify
36
+ searchIcon: mdiMagnify,
37
+ lockIcon: mdiLock
36
38
  }
37
39
  },
38
40
  breakpoint: {
@@ -1,11 +1,17 @@
1
1
  import { Model as ApiResourcesModel, apiResources } from '@afeefa/api-resources-client'
2
2
  import { mdiAlphaMCircle } from '@mdi/js'
3
3
 
4
+ import { ModelAdminConfig } from './ModelAdminConfig'
5
+
6
+ export { ModelAdminConfig }
7
+
4
8
  export class Model extends ApiResourcesModel {
5
9
  static resourceType = null
6
10
  static routeName = null
7
11
  static routeIdKey = 'id'
8
12
 
13
+ static config = null
14
+
9
15
  static getLink (action) {
10
16
  return (new this()).getLink(action)
11
17
  }
@@ -21,6 +27,13 @@ export class Model extends ApiResourcesModel {
21
27
  return null
22
28
  }
23
29
 
30
+ static get adminConfig () {
31
+ return new ModelAdminConfig()
32
+ .setIcon({
33
+ icon: mdiAlphaMCircle
34
+ })
35
+ }
36
+
24
37
  static icon = {
25
38
  icon: mdiAlphaMCircle,
26
39
  color: 'blue lighten-2'
@@ -0,0 +1,20 @@
1
+ export class ModelAdminConfig {
2
+ icon = null
3
+ selectedListConfig = null
4
+ selectableListConfig = null
5
+
6
+ setIcon (icon) {
7
+ this.icon = icon
8
+ return this
9
+ }
10
+
11
+ setSelectedListConfig (selectedListConfig) {
12
+ this.selectedListConfig = selectedListConfig
13
+ return this
14
+ }
15
+
16
+ setSelectableListConfig (selectableListConfig) {
17
+ this.selectableListConfig = selectableListConfig
18
+ return this
19
+ }
20
+ }
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <div>ICON</div>
3
+ </template>
4
+
5
+
6
+ <script>
7
+ export default {
8
+
9
+ }
10
+ </script>
11
+
12
+
13
+ <style lang="scss" scoped>
14
+ </style>