@fishawack/lab-velocity 2.0.0-beta.12 → 2.0.0-beta.13

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/README.md CHANGED
@@ -219,3 +219,120 @@ HYDRATE_LOGO_REVERSE Name of the logo when color scheme is reversed (default: to
219
219
  HYDRATE_REDIRECT Name of the route name to redirect to after login (default: index)
220
220
  HYDRATE_CONTACT Email for contact us button (default: mailto:det@avalerehealth.com)
221
221
  ```
222
+
223
+ ## Resources
224
+
225
+ To reduce the amount of template code needed you can import a fairly standard set of route files for index, show, create, edit & form via the resource module.
226
+
227
+ ##### routes.js
228
+
229
+ ```js
230
+ import { Resource } from "@fishawack/lab-velocity";
231
+
232
+ [
233
+ // ... other routes
234
+ ...Resource.routes(node, "posts"),
235
+ ];
236
+ ```
237
+
238
+ A third optional object can be passed for meta data that contains all of the overrides to customize the full rendering of these resource files. The default meta object is below.
239
+
240
+ ```js
241
+ // Name is the first param, properties is the optional second param
242
+ export function meta(name, properties = {}) {
243
+ const singular = properties.singular || name.slice(0, -1);
244
+
245
+ return merge(
246
+ {
247
+ name,
248
+ title: properties.title || name[0].toUpperCase() + name.slice(1),
249
+ singular,
250
+ label: singular,
251
+ multiLabel: name,
252
+ pageLink: name,
253
+ api: `/api/${name}`,
254
+ permissions: {
255
+ create: true,
256
+ edit: true,
257
+ },
258
+ searchable: {
259
+ value: "name",
260
+ label: `Search ${name}`,
261
+ },
262
+ form: {
263
+ component: null,
264
+ fields: () => ({}),
265
+ },
266
+ table: {
267
+ structure: [],
268
+ },
269
+ index: {
270
+ actions: [],
271
+ structure: [
272
+ {
273
+ render: ({ resource }) =>
274
+ h(VTableSorter, {
275
+ key: "PIndex",
276
+ "json-data": {
277
+ ...resource,
278
+ tableStructure: resource.table.structure,
279
+ },
280
+ defaults: resource.defaults,
281
+ "fixed-height": false,
282
+ "display-edit-action":
283
+ resource.permissions.create,
284
+ }),
285
+ },
286
+ ],
287
+ },
288
+ show: {
289
+ actions: [],
290
+ structure: [],
291
+ },
292
+ defaults: "",
293
+ icon: `icon-${singular}`,
294
+ },
295
+ properties,
296
+ );
297
+ }
298
+ ```
299
+
300
+ The structure arrays typically take a simple payload for their built in rendering but allow a render function for more complex layouts i.e
301
+
302
+ ```js
303
+ import { h, resolveComponent } from "vue";
304
+
305
+ {
306
+ // ...
307
+ show: {
308
+ structure: [
309
+ [
310
+ {
311
+ label: "Email",
312
+ key: "email",
313
+ },
314
+ {
315
+ label: "Company",
316
+ render: ({ model }) =>
317
+ h(resolveComponent("router-link"), {
318
+ class: "underline",
319
+ to: {
320
+ name: "companies.show",
321
+ params: { id: model.company_id },
322
+ },
323
+ text: model.company.name,
324
+ }),
325
+ },
326
+ ],
327
+ {
328
+ render: ({ model }) =>
329
+ h(VelFormRole, {
330
+ overrides: model.overrides_roles_and_permissions,
331
+ form: { roles: model.roles.map((d) => d.id) },
332
+ readonly: true,
333
+ }),
334
+ },
335
+ ],
336
+ },
337
+ }
338
+ ```
@@ -0,0 +1,76 @@
1
+ <template>
2
+ <VBreadcrumbs :items="breadcrumbs" class="mb-8" container-classes="m-0" />
3
+
4
+ <div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
5
+ <div class="grid__1/1">
6
+ <div class="grid__1/1 mb-4">
7
+ <h2 class="h1">Create {{ resource.singular }}</h2>
8
+ </div>
9
+ <div class="mt grid__1/2">
10
+ <component
11
+ :is="resource.form.component ?? 'XForm'"
12
+ ref="form"
13
+ :form="form"
14
+ :submit="submit"
15
+ :method="method"
16
+ :resource="resource"
17
+ />
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script>
24
+ import Form from "form-backend-validation";
25
+
26
+ export default {
27
+ components: {
28
+ VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
29
+ .default,
30
+ XForm: require("./partials/form.vue").default,
31
+ },
32
+
33
+ props: {
34
+ breadcrumbs: {
35
+ type: Array,
36
+ required: true,
37
+ },
38
+ resource: {
39
+ type: Object,
40
+ required: true,
41
+ },
42
+ },
43
+
44
+ data() {
45
+ return {
46
+ form: null,
47
+ method: "post",
48
+ };
49
+ },
50
+
51
+ beforeMount() {
52
+ this.form = new Form(this.resource.form.fields(this), {
53
+ resetOnSuccess: false,
54
+ });
55
+ },
56
+
57
+ methods: {
58
+ async submit() {
59
+ if (this.resource.form.submit) {
60
+ await this.resource.form.submit(this);
61
+ } else {
62
+ try {
63
+ let res = await this.form.post(`${this.resource.api}`);
64
+
65
+ this.$router.replace({
66
+ name: `${this.resource.name}.show`,
67
+ params: { id: res.data.id },
68
+ });
69
+ } catch (e) {
70
+ console.log(e);
71
+ }
72
+ }
73
+ },
74
+ },
75
+ };
76
+ </script>
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <VBreadcrumbs
3
+ :items="addBreadcrumbs"
4
+ class="mb-8"
5
+ container-classes="m-0"
6
+ />
7
+
8
+ <div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
9
+ <div class="grid__1/1">
10
+ <div class="grid__1/1 mb-4">
11
+ <h2 class="h1">Edit {{ resource.singular }}</h2>
12
+ </div>
13
+ <div class="grid__1/2">
14
+ <component
15
+ :is="resource.form.component ?? 'XForm'"
16
+ ref="form"
17
+ :form="form"
18
+ :submit="submit"
19
+ :method="method"
20
+ :resource="resource"
21
+ />
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </template>
26
+
27
+ <script>
28
+ import Form from "form-backend-validation";
29
+
30
+ export default {
31
+ components: {
32
+ VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
33
+ .default,
34
+ XForm: require("./partials/form.vue").default,
35
+ },
36
+
37
+ props: {
38
+ breadcrumbs: {
39
+ type: Array,
40
+ required: true,
41
+ },
42
+ resource: {
43
+ type: Object,
44
+ required: true,
45
+ },
46
+ },
47
+
48
+ data() {
49
+ return {
50
+ form: null,
51
+ model: null,
52
+ addBreadcrumbs: [...this.$props.breadcrumbs],
53
+ method: "patch",
54
+ };
55
+ },
56
+
57
+ beforeMount() {
58
+ this.form = new Form(this.resource.form.fields(this), {
59
+ resetOnSuccess: false,
60
+ });
61
+ },
62
+
63
+ async mounted() {
64
+ window.axios
65
+ .get(
66
+ `${this.resource.api}/${this.$route.params.id}?${this.resource.defaults}`,
67
+ )
68
+ .then((res) => {
69
+ this.model = res.data.data;
70
+
71
+ // Set initial form data
72
+ Object.entries(this.resource.form.fields(this)).forEach(
73
+ ([key, value]) => {
74
+ this.form[key] = value;
75
+ },
76
+ );
77
+
78
+ this.addBreadcrumbs.push({
79
+ href: {
80
+ name: `${this.resource.name}.show`,
81
+ param: this.model.id,
82
+ },
83
+ text: this.model.name,
84
+ });
85
+ });
86
+ },
87
+
88
+ methods: {
89
+ async submit() {
90
+ if (this.resource.form.submit) {
91
+ await this.resource.form.submit(this);
92
+ } else {
93
+ try {
94
+ let res = await this.form.patch(
95
+ `${this.resource.api}/${this.model.id}`,
96
+ );
97
+
98
+ this.$router.replace({
99
+ name: `${this.resource.name}.show`,
100
+ params: { id: res.data.id },
101
+ });
102
+ } catch (e) {
103
+ console.log(e);
104
+ }
105
+ }
106
+ },
107
+ },
108
+ };
109
+ </script>
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <VBreadcrumbs :items="breadcrumbs" />
3
+
4
+ <div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
5
+ <div class="grid__1/1">
6
+ <h2 class="h1 pb-4">
7
+ {{ breadcrumbs[breadcrumbs.length - 1].text }}
8
+ </h2>
9
+
10
+ <div class="flex gap items-center justify-end">
11
+ <template
12
+ v-for="(row, index) in resource.index.actions"
13
+ :key="index"
14
+ >
15
+ <template v-if="row.render">
16
+ <component :is="row.render(this)" />
17
+ </template>
18
+ </template>
19
+ </div>
20
+
21
+ <template
22
+ v-for="(row, index) in resource.index.structure"
23
+ :key="index"
24
+ >
25
+ <template v-if="row.render">
26
+ <component :is="row.render(this)" />
27
+ </template>
28
+ </template>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <script>
34
+ export default {
35
+ components: {
36
+ VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
37
+ .default,
38
+ },
39
+
40
+ props: {
41
+ breadcrumbs: {
42
+ type: Array,
43
+ required: true,
44
+ },
45
+ resource: {
46
+ type: Object,
47
+ required: true,
48
+ },
49
+ },
50
+ };
51
+ </script>
@@ -0,0 +1,53 @@
1
+ <!-- eslint-disable vue/no-mutating-props -->
2
+ <template>
3
+ <form @submit.prevent="submit">
4
+ <template v-for="(item, index) in resource.form.structure" :key="index">
5
+ <component
6
+ :is="item.render ? item.render(this) : 'VelBasic'"
7
+ v-model="form[item.key]"
8
+ :type="item.type || 'text'"
9
+ :error="form.errors"
10
+ :name="item.key"
11
+ :placeholder="
12
+ item.placeholder ||
13
+ item.key[0].toUpperCase() + item.key.slice(1)
14
+ "
15
+ :label="
16
+ item.label || item.key[0].toUpperCase() + item.key.slice(1)
17
+ "
18
+ v-bind="item"
19
+ />
20
+ </template>
21
+
22
+ <VelFormFooter :loading="form.processing" />
23
+ </form>
24
+ </template>
25
+
26
+ <script>
27
+ export default {
28
+ components: {
29
+ VelFormFooter: require("../../../../components/layout/FormFooter.vue")
30
+ .default,
31
+ VelBasic: require("../../../../components/form/basic.vue").default,
32
+ },
33
+
34
+ props: {
35
+ form: {
36
+ required: true,
37
+ type: Object,
38
+ },
39
+ submit: {
40
+ required: true,
41
+ type: Function,
42
+ },
43
+ method: {
44
+ type: String,
45
+ default: "post",
46
+ },
47
+ resource: {
48
+ required: true,
49
+ type: Object,
50
+ },
51
+ },
52
+ };
53
+ </script>
@@ -0,0 +1,145 @@
1
+ <template>
2
+ <VBreadcrumbs
3
+ :items="addBreadcrumbs"
4
+ class="mb-8"
5
+ container-classes="m-0"
6
+ />
7
+
8
+ <div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
9
+ <div class="grid__1/1">
10
+ <template v-if="model">
11
+ <div class="bg-0 p-3 box-shadow-1 border-r-4 mb-6">
12
+ <VelPageHeader
13
+ :icon="resource.icon"
14
+ :title="`${model.name} ${model.last_name ?? ''}`"
15
+ >
16
+ <template
17
+ v-for="(row, index) in resource.show.actions"
18
+ :key="index"
19
+ >
20
+ <template v-if="row.render">
21
+ <component :is="row.render(this)" />
22
+ </template>
23
+ </template>
24
+ <VelButton
25
+ v-if="resource.permissions.edit"
26
+ tag="a"
27
+ type="primary"
28
+ @click="
29
+ $router.push({
30
+ name: `${resource.name}.edit`,
31
+ param: model.id,
32
+ })
33
+ "
34
+ >
35
+ <GIcon
36
+ class="fill-0 mr-0.5 icon--0.5"
37
+ name="icon-edit"
38
+ embed
39
+ artboard
40
+ />
41
+ Edit {{ resource.singular }}
42
+ </VelButton>
43
+ </VelPageHeader>
44
+
45
+ <hr class="my-3 hr-muted" />
46
+
47
+ <template
48
+ v-for="(row, index) in resource.show.structure"
49
+ :key="index"
50
+ >
51
+ <template v-if="row.render">
52
+ <component :is="row.render(this)" />
53
+ </template>
54
+ <el-descriptions v-else border column="1">
55
+ <el-descriptions-item
56
+ v-for="(item, i) in row"
57
+ :key="i"
58
+ :label-width="'20%'"
59
+ >
60
+ <template #label>
61
+ {{
62
+ item.label ||
63
+ item.key[0].toUpperCase() +
64
+ item.key.slice(1)
65
+ }}
66
+ </template>
67
+
68
+ <template v-if="item.render">
69
+ <component :is="item.render(this)" />
70
+ </template>
71
+ <template v-else>
72
+ {{ model[item.key] }}
73
+ </template>
74
+ </el-descriptions-item>
75
+ </el-descriptions>
76
+
77
+ <hr
78
+ v-if="index < resource.show.structure.length - 1"
79
+ class="my-3 hr-muted"
80
+ />
81
+ </template>
82
+ </div>
83
+ </template>
84
+
85
+ <div v-else class="absolute transform-center text-center">
86
+ <VelSpinner />
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </template>
91
+
92
+ <script>
93
+ import axios from "axios";
94
+ import VelSpinner from "../../../components/form/Spinner.vue";
95
+ import VelButton from "../../../components/basic/Button.vue";
96
+ import { ElDescriptions, ElDescriptionsItem } from "element-plus";
97
+
98
+ export default {
99
+ components: {
100
+ VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
101
+ .default,
102
+ VelPageHeader: require("../../../components/layout/PageHeader.vue")
103
+ .default,
104
+ VelSpinner,
105
+ VelButton,
106
+ ElDescriptions,
107
+ ElDescriptionsItem,
108
+ },
109
+
110
+ props: {
111
+ breadcrumbs: {
112
+ type: Array,
113
+ required: true,
114
+ },
115
+ resource: {
116
+ type: Object,
117
+ required: true,
118
+ },
119
+ },
120
+
121
+ data() {
122
+ return {
123
+ model: null,
124
+ addBreadcrumbs: [...this.$props.breadcrumbs],
125
+ };
126
+ },
127
+
128
+ mounted() {
129
+ axios
130
+ .get(
131
+ `${this.resource.api}/${this.$route.params.id}?${this.resource.defaults}`,
132
+ )
133
+ .then((res) => {
134
+ this.model = res.data.data;
135
+ this.addBreadcrumbs.push({
136
+ href: {
137
+ name: `${this.resource.name}.show`,
138
+ param: this.model.id,
139
+ },
140
+ text: this.model.name,
141
+ });
142
+ });
143
+ },
144
+ };
145
+ </script>
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+
3
+ import { merge } from "lodash";
4
+
5
+ import { h } from "vue";
6
+
7
+ import VTableSorter from "../../components/layout/TableSorter.vue";
8
+
9
+ export function meta(name, properties = {}) {
10
+ const singular = properties.singular || name.slice(0, -1);
11
+
12
+ return merge(
13
+ {
14
+ name,
15
+ title: properties.title || name[0].toUpperCase() + name.slice(1),
16
+ singular,
17
+ label: singular,
18
+ multiLabel: name,
19
+ pageLink: name,
20
+ api: `/api/${name}`,
21
+ permissions: {
22
+ create: true,
23
+ edit: true,
24
+ },
25
+ searchable: {
26
+ value: "name",
27
+ label: `Search ${name}`,
28
+ },
29
+ form: {
30
+ component: null,
31
+ fields: () => ({}),
32
+ },
33
+ table: {
34
+ structure: [],
35
+ },
36
+ index: {
37
+ actions: [],
38
+ structure: [
39
+ {
40
+ render: ({ resource }) =>
41
+ h(VTableSorter, {
42
+ key: "PIndex",
43
+ "json-data": {
44
+ ...resource,
45
+ tableStructure: resource.table.structure,
46
+ },
47
+ defaults: resource.defaults,
48
+ "fixed-height": false,
49
+ "display-edit-action":
50
+ resource.permissions.create,
51
+ }),
52
+ },
53
+ ],
54
+ },
55
+ show: {
56
+ actions: [],
57
+ structure: [],
58
+ },
59
+ defaults: "",
60
+ icon: `icon-${singular}`,
61
+ },
62
+ properties,
63
+ );
64
+ }
65
+
66
+ // Export resource
67
+ export function routes(node, name, properties = {}) {
68
+ return [
69
+ {
70
+ path: `/${name}`,
71
+ component: node ? "" : require("../resource/parent.vue").default,
72
+ meta: {
73
+ resource: meta(name, properties),
74
+ },
75
+ children: [
76
+ {
77
+ path: "",
78
+ component: node
79
+ ? ""
80
+ : require("../resource/Children/index.vue").default,
81
+ name: `${name}.index`,
82
+ },
83
+ {
84
+ path: "create",
85
+ component: node
86
+ ? ""
87
+ : require("../resource/Children/create.vue").default,
88
+ name: `${name}.create`,
89
+ },
90
+ {
91
+ path: ":id",
92
+ component: node
93
+ ? ""
94
+ : require("../resource/Children/show.vue").default,
95
+ name: `${name}.show`,
96
+ },
97
+ {
98
+ path: ":id/edit",
99
+ component: node
100
+ ? ""
101
+ : require("../resource/Children/edit.vue").default,
102
+ name: `${name}.edit`,
103
+ },
104
+ ],
105
+ },
106
+ ];
107
+ }
108
+
109
+ export default {
110
+ routes,
111
+ meta,
112
+ };
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <PageTitle :title="resource.title" />
3
+
4
+ <router-view
5
+ :key="$route.path"
6
+ :breadcrumbs="breadcrumbs"
7
+ :resource="resource"
8
+ />
9
+ </template>
10
+
11
+ <script>
12
+ import PageTitle from "../../components/layout/pageTitle.vue";
13
+
14
+ export default {
15
+ components: {
16
+ PageTitle,
17
+ },
18
+
19
+ computed: {
20
+ resource() {
21
+ return this.$route.meta.resource;
22
+ },
23
+ breadcrumbs() {
24
+ return [
25
+ {
26
+ href: {
27
+ name: "index",
28
+ },
29
+ text: "Home",
30
+ },
31
+ {
32
+ href: {
33
+ name: `${this.resource.name}.index`,
34
+ },
35
+ text: this.resource.title,
36
+ },
37
+ ];
38
+ },
39
+ },
40
+ };
41
+ </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishawack/lab-velocity",
3
- "version": "2.0.0-beta.12",
3
+ "version": "2.0.0-beta.13",
4
4
  "description": "Avalere Health branded style system",
5
5
  "scripts": {
6
6
  "setup": "npm ci || npm i && npm run content",
@@ -64,7 +64,8 @@
64
64
  "*.scss",
65
65
  "components",
66
66
  "_Build/vue/components",
67
- "_Build/vue/modules/AuthModule"
67
+ "_Build/vue/modules/AuthModule",
68
+ "_Build/vue/modules/resource"
68
69
  ],
69
70
  "main": "index.js",
70
71
  "release": {