@fishawack/lab-velocity 2.0.0-beta.22 → 2.0.0-beta.24

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.
@@ -67,13 +67,13 @@
67
67
  :target-action="
68
68
  (item) => ({
69
69
  name: `${jsonData.slug}.show`,
70
- params: { id: item.id },
70
+ params: { [idKey]: item.id },
71
71
  })
72
72
  "
73
73
  :edit-action="
74
74
  (item) => ({
75
75
  name: `${jsonData.slug}.edit`,
76
- params: { id: item.id },
76
+ params: { [idKey]: item.id },
77
77
  })
78
78
  "
79
79
  :display-actions="displayActions"
@@ -149,6 +149,10 @@ export default {
149
149
  type: Boolean,
150
150
  default: true,
151
151
  },
152
+ idKey: {
153
+ type: String,
154
+ default: "id",
155
+ },
152
156
  },
153
157
 
154
158
  data() {
@@ -150,6 +150,7 @@ export default [
150
150
  }),
151
151
  ({ model, $store }) => {
152
152
  const resource = meta(...userResource);
153
+
153
154
  return h(VelTableSorter, {
154
155
  key: "PIndex",
155
156
  "json-data": {
@@ -157,6 +158,7 @@ export default [
157
158
  tableStructure: resource.table.structure,
158
159
  },
159
160
  defaults: `include=company&filter[company_id]=${model.id}`,
161
+ idKey: resource.id,
160
162
  "fixed-height": false,
161
163
  "display-create-action":
162
164
  $store.getters.can("write users"),
@@ -1,10 +1,10 @@
1
1
  import VelFormRole from "../../../../components/layout/FormRole.vue";
2
2
  import Chip from "../../../../components/layout/Chip.vue";
3
3
  import Chips from "../../../../components/layout/Chips.vue";
4
- import VelTableSorter from "../../../../components/layout/TableSorter.vue";
5
4
  import VelRoleLegend from "../../../../components/layout/RoleLegend.vue";
6
5
  import component from "./form.vue";
7
- import { defaultResource } from "../../../resource/index.js";
6
+ import companyResource from "../PCompanies/resource.js";
7
+ import { defaultResource, meta } from "../../../resource/index.js";
8
8
 
9
9
  import { ElMessageBox } from "element-plus";
10
10
  import { ElNotification } from "element-plus";
@@ -34,7 +34,7 @@ export default [
34
34
  delete: ({ $store }) => $store.getters.can("delete companies"),
35
35
  },
36
36
  form: {
37
- async submit({ model, form, $router, $store, method }) {
37
+ async submit({ model, form, $router, $store, method, resource }) {
38
38
  try {
39
39
  if (method === "post") {
40
40
  if (form.set_password) {
@@ -57,7 +57,7 @@ export default [
57
57
  .then(() => {
58
58
  $router.replace({
59
59
  name: "users.show",
60
- params: { id: res.data.id },
60
+ params: { [resource.id]: res.data.id },
61
61
  });
62
62
  })
63
63
  .catch(() => {});
@@ -71,7 +71,7 @@ export default [
71
71
 
72
72
  $router.replace({
73
73
  name: "users.show",
74
- params: { id: res.data.id },
74
+ params: { [resource.id]: res.data.id },
75
75
  });
76
76
  }
77
77
  } else {
@@ -84,7 +84,7 @@ export default [
84
84
 
85
85
  $router.replace({
86
86
  name: "users.show",
87
- params: { id: res.data.id },
87
+ params: { [resource.id]: res.data.id },
88
88
  });
89
89
  }
90
90
  } catch (e) {
@@ -131,7 +131,10 @@ export default [
131
131
  class: "underline",
132
132
  to: {
133
133
  name: "companies.show",
134
- params: { id: model.company_id },
134
+ params: {
135
+ [meta(...companyResource).id]:
136
+ model.company_id,
137
+ },
135
138
  },
136
139
  text: model.company.name,
137
140
  }),
@@ -171,7 +174,10 @@ export default [
171
174
  class: "underline",
172
175
  to: {
173
176
  name: "companies.show",
174
- params: { id: model.company_id },
177
+ params: {
178
+ [meta(...companyResource).id]:
179
+ model.company_id,
180
+ },
175
181
  },
176
182
  text: model.company.name,
177
183
  }),
@@ -1,6 +1,4 @@
1
1
  <template>
2
- <VBreadcrumbs :items="breadcrumbs" class="mb-8" container-classes="m-0" />
3
-
4
2
  <div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
5
3
  <div class="grid__1/1">
6
4
  <div class="grid__1/1 mb-4">
@@ -25,16 +23,10 @@ import Form from "form-backend-validation";
25
23
 
26
24
  export default {
27
25
  components: {
28
- VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
29
- .default,
30
26
  XForm: require("./partials/form.vue").default,
31
27
  },
32
28
 
33
29
  props: {
34
- breadcrumbs: {
35
- type: Array,
36
- required: true,
37
- },
38
30
  resource: {
39
31
  type: Object,
40
32
  required: true,
@@ -60,11 +52,13 @@ export default {
60
52
  await this.resource.form.submit(this);
61
53
  } else {
62
54
  try {
63
- let res = await this.form.post(`${this.resource.api}`);
55
+ let res = await this.form.post(
56
+ `${this.resource.api(this)}`,
57
+ );
64
58
 
65
59
  this.$router.replace({
66
60
  name: `${this.resource.slug}.show`,
67
- params: { id: res.data.id },
61
+ params: { [this.resource.id]: res.data.id },
68
62
  });
69
63
  } catch (e) {
70
64
  console.log(e);
@@ -1,10 +1,4 @@
1
1
  <template>
2
- <VBreadcrumbs
3
- :items="addBreadcrumbs"
4
- class="mb-8"
5
- container-classes="m-0"
6
- />
7
-
8
2
  <div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
9
3
  <div class="grid__1/1">
10
4
  <div class="grid__1/1 mb-4">
@@ -30,16 +24,10 @@ import Form from "form-backend-validation";
30
24
 
31
25
  export default {
32
26
  components: {
33
- VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
34
- .default,
35
27
  XForm: require("./partials/form.vue").default,
36
28
  },
37
29
 
38
30
  props: {
39
- breadcrumbs: {
40
- type: Array,
41
- required: true,
42
- },
43
31
  resource: {
44
32
  type: Object,
45
33
  required: true,
@@ -50,7 +38,6 @@ export default {
50
38
  return {
51
39
  form: null,
52
40
  model: null,
53
- addBreadcrumbs: [...this.$props.breadcrumbs],
54
41
  method: "patch",
55
42
  };
56
43
  },
@@ -64,7 +51,7 @@ export default {
64
51
  async mounted() {
65
52
  window.axios
66
53
  .get(
67
- `${this.resource.api}/${this.$route.params.id}?${this.resource.defaults}`,
54
+ `${this.resource.api(this)}/${this.$route.params[this.resource.id]}?${this.resource.defaults}`,
68
55
  )
69
56
  .then((res) => {
70
57
  this.model = res.data.data;
@@ -75,14 +62,6 @@ export default {
75
62
  this.form[key] = value;
76
63
  },
77
64
  );
78
-
79
- this.addBreadcrumbs.push({
80
- href: {
81
- name: `${this.resource.slug}.show`,
82
- param: this.model.id,
83
- },
84
- text: this.model.name,
85
- });
86
65
  });
87
66
  },
88
67
 
@@ -93,12 +72,12 @@ export default {
93
72
  } else {
94
73
  try {
95
74
  let res = await this.form.patch(
96
- `${this.resource.api}/${this.model.id}`,
75
+ `${this.resource.api(this)}/${this.model.id}`,
97
76
  );
98
77
 
99
78
  this.$router.replace({
100
79
  name: `${this.resource.slug}.show`,
101
- params: { id: res.data.id },
80
+ params: { [this.resource.id]: res.data.id },
102
81
  });
103
82
  } catch (e) {
104
83
  console.log(e);
@@ -1,12 +1,6 @@
1
1
  <template>
2
- <VBreadcrumbs :items="breadcrumbs" />
3
-
4
2
  <div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
5
3
  <div class="grid__1/1">
6
- <h2 class="h1 pb-4">
7
- {{ breadcrumbs[breadcrumbs.length - 1].text }}
8
- </h2>
9
-
10
4
  <div class="flex gap items-center justify-end">
11
5
  <template
12
6
  v-for="(render, index) in resource.index.actions"
@@ -28,20 +22,15 @@
28
22
 
29
23
  <script>
30
24
  export default {
31
- components: {
32
- VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
33
- .default,
34
- },
35
-
36
25
  props: {
37
- breadcrumbs: {
38
- type: Array,
39
- required: true,
40
- },
41
26
  resource: {
42
27
  type: Object,
43
28
  required: true,
44
29
  },
30
+ depth: {
31
+ type: Number,
32
+ required: true,
33
+ },
45
34
  },
46
35
  };
47
36
  </script>
@@ -1,47 +1,44 @@
1
1
  <template>
2
- <VBreadcrumbs
3
- :items="addBreadcrumbs"
4
- class="mb-8"
5
- container-classes="m-0"
6
- />
2
+ <!-- Render nested router view if this isn't the deepest route -->
3
+ <router-view v-if="!deepestRoute" v-slot="{ Component }">
4
+ <component :is="Component" :depth="depth + 1" />
5
+ </router-view>
7
6
 
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
- >
7
+ <template v-else>
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="(render, index) in resource.show.actions"
18
+ :key="index"
19
+ >
20
+ <component :is="render(this)" />
21
+ </template>
22
+ </VelPageHeader>
23
+ <hr class="my-3 hr-muted" />
16
24
  <template
17
- v-for="(render, index) in resource.show.actions"
25
+ v-for="(render, index) in resource.show.layout"
18
26
  :key="index"
19
27
  >
20
28
  <component :is="render(this)" />
29
+ <hr
30
+ v-if="index < resource.show.layout.length - 1"
31
+ class="my-3 hr-muted"
32
+ />
21
33
  </template>
22
- </VelPageHeader>
23
-
24
- <hr class="my-3 hr-muted" />
25
-
26
- <template
27
- v-for="(render, index) in resource.show.layout"
28
- :key="index"
29
- >
30
- <component :is="render(this)" />
31
-
32
- <hr
33
- v-if="index < resource.show.layout.length - 1"
34
- class="my-3 hr-muted"
35
- />
36
- </template>
34
+ </div>
35
+ </template>
36
+ <div v-else class="absolute transform-center text-center">
37
+ <VelSpinner />
37
38
  </div>
38
- </template>
39
-
40
- <div v-else class="absolute transform-center text-center">
41
- <VelSpinner />
42
39
  </div>
43
40
  </div>
44
- </div>
41
+ </template>
45
42
  </template>
46
43
 
47
44
  <script>
@@ -51,8 +48,6 @@ import VelButton from "../../../components/basic/Button.vue";
51
48
 
52
49
  export default {
53
50
  components: {
54
- VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
55
- .default,
56
51
  VelPageHeader: require("../../../components/layout/PageHeader.vue")
57
52
  .default,
58
53
  VelSpinner,
@@ -68,29 +63,36 @@ export default {
68
63
  type: Object,
69
64
  required: true,
70
65
  },
66
+ depth: {
67
+ type: Number,
68
+ required: true,
69
+ },
71
70
  },
72
71
 
73
72
  data() {
74
73
  return {
75
74
  model: null,
76
- addBreadcrumbs: [...this.$props.breadcrumbs],
77
75
  };
78
76
  },
79
77
 
78
+ computed: {
79
+ // This boolean helps determine if we are the final depth of route being rendered
80
+ deepestRoute() {
81
+ return this.depth === this.$route.matched.length;
82
+ },
83
+ },
84
+
80
85
  mounted() {
86
+ if (!this.deepestRoute) {
87
+ return;
88
+ }
89
+
81
90
  axios
82
91
  .get(
83
- `${this.resource.api}/${this.$route.params.id}?${this.resource.defaults}`,
92
+ `${this.resource.api(this)}/${this.$route.params[`${this.resource.slug}Id`]}?${this.resource.defaults}`,
84
93
  )
85
94
  .then((res) => {
86
95
  this.model = res.data.data;
87
- this.addBreadcrumbs.push({
88
- href: {
89
- name: `${this.resource.slug}.show`,
90
- param: this.model.id,
91
- },
92
- text: this.model.name,
93
- });
94
96
  });
95
97
  },
96
98
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- import { merge, kebabCase } from "lodash";
3
+ import { merge, kebabCase, cloneDeepWith } from "lodash";
4
4
  import axios from "axios";
5
5
  import { h, resolveComponent } from "vue";
6
6
 
@@ -12,6 +12,7 @@ export const defaultResource = meta();
12
12
 
13
13
  export function meta(name = "default", properties = {}) {
14
14
  const singular = properties.singular || name.slice(0, -1);
15
+ const slug = properties.slug || kebabCase(name);
15
16
 
16
17
  return merge(
17
18
  {
@@ -20,8 +21,9 @@ export function meta(name = "default", properties = {}) {
20
21
  singular,
21
22
  label: singular,
22
23
  multiLabel: name,
23
- slug: properties?.slug || kebabCase(name),
24
- api: `/api/${properties?.slug || kebabCase(name)}`,
24
+ slug,
25
+ id: properties.id || `${slug}Id`,
26
+ api: () => `/api/${properties.slug || kebabCase(name)}`,
25
27
  permissions: {
26
28
  create: () => true,
27
29
  edit: () => true,
@@ -61,8 +63,10 @@ export function meta(name = "default", properties = {}) {
61
63
  "json-data": {
62
64
  ...resource,
63
65
  tableStructure: resource.table.structure,
66
+ api: resource.api(props),
64
67
  },
65
68
  defaults: resource.defaults,
69
+ idKey: resource.id,
66
70
  "fixed-height": false,
67
71
  "display-create-action":
68
72
  resource.permissions.create(props),
@@ -86,7 +90,9 @@ export function meta(name = "default", properties = {}) {
86
90
  onClick: () => {
87
91
  $router.push({
88
92
  name: `${resource.slug}.edit`,
89
- params: { id: model.id },
93
+ params: {
94
+ [resource.id]: model.id,
95
+ },
90
96
  });
91
97
  },
92
98
  },
@@ -115,7 +121,7 @@ export function meta(name = "default", properties = {}) {
115
121
  confirmButtonType: "danger",
116
122
  onConfirm: async () => {
117
123
  await axios.delete(
118
- `${resource.api}/${model.id}`,
124
+ `${resource.api(props)}/${model.id}`,
119
125
  );
120
126
 
121
127
  $router.push({
@@ -228,18 +234,25 @@ export function columns(columns = []) {
228
234
  }
229
235
 
230
236
  // Export resource
231
- export function routes(node, name, properties = {}) {
237
+ export function routes(
238
+ node,
239
+ name,
240
+ properties = {},
241
+ children = [],
242
+ isChild = false,
243
+ ) {
232
244
  const resource = meta(name, properties);
233
245
 
234
246
  return [
235
247
  {
236
- path: `/${resource.slug}`,
248
+ path: `${isChild ? "" : "/"}${resource.slug}`,
237
249
  component: node ? "" : require("../resource/parent.vue").default,
238
250
  name,
239
251
  meta: {
240
252
  resource,
241
253
  title: resource.title,
242
254
  icon: resource.icon,
255
+ breadcrumb: () => resource.title,
243
256
  },
244
257
  children: [
245
258
  {
@@ -257,18 +270,34 @@ export function routes(node, name, properties = {}) {
257
270
  name: `${resource.slug}.create`,
258
271
  },
259
272
  {
260
- path: ":id",
273
+ path: `:${resource.id}`,
261
274
  component: node
262
275
  ? ""
263
276
  : require("../resource/Children/show.vue").default,
264
277
  name: `${resource.slug}.show`,
278
+ // Remove leading / for nested routes or they'll resolve to the root of the site
279
+ children: cloneDeepWith(children, (value, key) => {
280
+ if (
281
+ key === "path" &&
282
+ typeof value === "string" &&
283
+ value.startsWith("/")
284
+ ) {
285
+ return value.slice(1);
286
+ }
287
+ }),
288
+ meta: {
289
+ breadcrumb: ({ $route }) => $route.params[resource.id],
290
+ },
265
291
  },
266
292
  {
267
- path: ":id/edit",
293
+ path: `:${resource.id}/edit`,
268
294
  component: node
269
295
  ? ""
270
296
  : require("../resource/Children/edit.vue").default,
271
297
  name: `${resource.slug}.edit`,
298
+ meta: {
299
+ breadcrumb: ({ $route }) => $route.params[resource.id],
300
+ },
272
301
  },
273
302
  ],
274
303
  },
@@ -279,4 +308,5 @@ export default {
279
308
  routes,
280
309
  meta,
281
310
  columns,
311
+ defaultResource,
282
312
  };
@@ -1,19 +1,39 @@
1
1
  <template>
2
- <PageTitle :title="resource.title" />
2
+ <!-- Only render title once at the shallowest route -->
3
+ <template v-if="depth === 1">
4
+ <VelPageTitle :title="resource.title" />
5
+ <VelBreadcrumbs
6
+ :items="breadcrumbs"
7
+ class="mb-8"
8
+ container-classes="m-0"
9
+ />
10
+ </template>
3
11
 
4
12
  <router-view
5
13
  :key="$route.path"
14
+ v-slot="{ Component }"
6
15
  :breadcrumbs="breadcrumbs"
7
16
  :resource="resource"
8
- />
17
+ >
18
+ <component :is="Component" :depth="depth + 1" />
19
+ </router-view>
9
20
  </template>
10
21
 
11
22
  <script>
12
- import PageTitle from "../../components/layout/pageTitle.vue";
23
+ import VelPageTitle from "../../components/layout/pageTitle.vue";
24
+ import VelBreadcrumbs from "../../components/layout/Breadcrumbs.vue";
13
25
 
14
26
  export default {
15
27
  components: {
16
- PageTitle,
28
+ VelPageTitle,
29
+ VelBreadcrumbs,
30
+ },
31
+
32
+ props: {
33
+ depth: {
34
+ type: Number,
35
+ default: 1,
36
+ },
17
37
  },
18
38
 
19
39
  computed: {
@@ -23,18 +43,20 @@ export default {
23
43
  breadcrumbs() {
24
44
  return [
25
45
  {
26
- href: {
27
- name: "index",
28
- },
29
46
  text: "Home",
47
+ href: { name: "index" },
30
48
  },
31
- {
32
- href: {
33
- name: `${this.resource.slug}.index`,
34
- },
35
- text: this.resource.title,
36
- },
37
- ];
49
+ ].concat(
50
+ this.$route.matched
51
+ .filter((route) => route.meta?.breadcrumb)
52
+ .map((route) => ({
53
+ text: route.meta.breadcrumb(this),
54
+ href: {
55
+ name: route.name,
56
+ params: this.$route.params,
57
+ },
58
+ })),
59
+ );
38
60
  },
39
61
  },
40
62
  };
package/index.js CHANGED
@@ -26,6 +26,8 @@ export { default as Wysiwyg2 } from "./_Build/vue/components/form/Wysiwyg2.vue";
26
26
  export { default as Upload } from "./_Build/vue/components/form/Upload.vue";
27
27
  export { default as InputNumber } from "./_Build/vue/components/form/InputNumber.vue";
28
28
 
29
+ export { default as RoleLegend } from "./_Build/vue/components/layout/RoleLegend.vue";
30
+ export { default as TableSorter } from "./_Build/vue/components/layout/TableSorter.vue";
29
31
  export { default as Chip } from "./_Build/vue/components/layout/Chip.vue";
30
32
  export { default as Chips } from "./_Build/vue/components/layout/Chips.vue";
31
33
  export { default as SideBar } from "./_Build/vue/components/layout/SideBar.vue";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishawack/lab-velocity",
3
- "version": "2.0.0-beta.22",
3
+ "version": "2.0.0-beta.24",
4
4
  "description": "Avalere Health branded style system",
5
5
  "scripts": {
6
6
  "setup": "npm ci || npm i && npm run content",