@fishawack/lab-velocity 1.11.1 → 2.0.0-beta.2

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 (132) hide show
  1. package/README.md +13 -7
  2. package/_Build/vue/components/Icon.vue +33 -0
  3. package/_Build/vue/components/Svg.vue +45 -0
  4. package/{basic → _Build/vue/components/basic}/Button.vue +16 -18
  5. package/{basic → _Build/vue/components/basic}/link.vue +8 -7
  6. package/{form → _Build/vue/components/form}/Cascader.vue +13 -13
  7. package/{form → _Build/vue/components/form}/CheckboxGroup.vue +28 -6
  8. package/{form → _Build/vue/components/form}/DatePicker.vue +23 -7
  9. package/{form → _Build/vue/components/form}/InputNumber.vue +1 -2
  10. package/{form → _Build/vue/components/form}/Select.vue +8 -9
  11. package/{form → _Build/vue/components/form}/Switch.vue +2 -2
  12. package/{form → _Build/vue/components/form}/Upload.vue +4 -6
  13. package/{form → _Build/vue/components/form}/Wysiwyg.vue +14 -14
  14. package/_Build/vue/components/form/Wysiwyg2.vue +577 -0
  15. package/{form → _Build/vue/components/form}/basic.vue +25 -7
  16. package/{form → _Build/vue/components/form}/file.vue +1 -1
  17. package/{form → _Build/vue/components/form}/input.js +2 -2
  18. package/{form → _Build/vue/components/form}/input.vue +31 -11
  19. package/{layout → _Build/vue/components/layout}/Alert.vue +10 -10
  20. package/_Build/vue/components/layout/Footer.vue +50 -0
  21. package/{layout → _Build/vue/components/layout}/Header.vue +5 -7
  22. package/_Build/vue/components/layout/Loader.vue +59 -0
  23. package/{layout → _Build/vue/components/layout}/Tooltip.vue +12 -12
  24. package/{layout → _Build/vue/components/layout}/pageTitle.vue +4 -4
  25. package/{layout → _Build/vue/components/layout}/sideBar.vue +4 -6
  26. package/{navigation → _Build/vue/components/navigation}/Breadcrumbs.vue +15 -10
  27. package/{navigation → _Build/vue/components/navigation}/BreadcrumbsItem.vue +6 -6
  28. package/_Build/vue/components/navigation/Menu.vue +14 -0
  29. package/_Build/vue/components/navigation/MenuItem.vue +20 -0
  30. package/_Build/vue/components/navigation/MenuItemGroup.vue +20 -0
  31. package/_Build/vue/components/navigation/SubMenu.vue +20 -0
  32. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/Upload/upload.vue +251 -0
  33. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/create.vue +62 -0
  34. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/edit.vue +98 -0
  35. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/index.vue +90 -0
  36. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/partials/form.vue +173 -0
  37. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/show.vue +262 -0
  38. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/parent.vue +36 -0
  39. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/create.vue +112 -0
  40. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/edit.vue +103 -0
  41. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/index.vue +112 -0
  42. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/partials/form.vue +169 -0
  43. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/show.vue +120 -0
  44. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/parent.vue +36 -0
  45. package/{AuthModule → _Build/vue/modules/AuthModule}/components/AuthModal.vue +30 -35
  46. package/_Build/vue/modules/AuthModule/components/Chip.vue +70 -0
  47. package/_Build/vue/modules/AuthModule/components/Chips.vue +26 -0
  48. package/_Build/vue/modules/AuthModule/components/FormRole.vue +115 -0
  49. package/_Build/vue/modules/AuthModule/components/VBreadcrumbs.vue +32 -0
  50. package/_Build/vue/modules/AuthModule/components/VFormFooter.vue +46 -0
  51. package/_Build/vue/modules/AuthModule/components/VPageHeader.vue +38 -0
  52. package/_Build/vue/modules/AuthModule/components/VPasswordValidation.vue +106 -0
  53. package/_Build/vue/modules/AuthModule/components/VRoleLegend.vue +43 -0
  54. package/_Build/vue/modules/AuthModule/components/VTable.vue +127 -0
  55. package/_Build/vue/modules/AuthModule/components/VTableSorter.vue +240 -0
  56. package/_Build/vue/modules/AuthModule/js/FakeAPI.js +78 -0
  57. package/_Build/vue/modules/AuthModule/js/axios.js +62 -0
  58. package/_Build/vue/modules/AuthModule/js/router.js +295 -0
  59. package/_Build/vue/modules/AuthModule/js/store.js +62 -0
  60. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/account-exists.vue +5 -7
  61. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/change-password.vue +28 -25
  62. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/container.vue +4 -8
  63. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/expired-reset.vue +13 -11
  64. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/expired-verification.vue +12 -12
  65. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/force-reset.vue +36 -26
  66. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/forgot.vue +11 -7
  67. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/login.vue +20 -15
  68. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/logincallback.vue +5 -10
  69. package/_Build/vue/modules/AuthModule/routes/loginheadless.vue +21 -0
  70. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/loginsso.vue +32 -25
  71. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/logout.vue +6 -6
  72. package/_Build/vue/modules/AuthModule/routes/logoutheadless.vue +27 -0
  73. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/register.vue +43 -25
  74. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/reset.vue +24 -14
  75. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/success-forgot.vue +14 -12
  76. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/success-reset.vue +4 -4
  77. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/success-verify.vue +10 -7
  78. package/{AuthModule → _Build/vue/modules/AuthModule}/routes/verify.vue +15 -12
  79. package/_base.scss +1 -1
  80. package/_defaults.scss +2 -3
  81. package/_variables.scss +16 -18
  82. package/general.scss +1 -2
  83. package/index.js +38 -30
  84. package/package.json +103 -102
  85. package/vendor.scss +2 -2
  86. package/AuthModule/components/VPasswordValidation.vue +0 -66
  87. package/AuthModule/js/AuthAxios.js +0 -59
  88. package/AuthModule/js/AuthRoutes.js +0 -186
  89. package/AuthModule/js/AuthStore.js +0 -99
  90. package/AuthModule/js/FakeAPI.js +0 -84
  91. package/AuthModule/routes/loginheadless.vue +0 -16
  92. package/Icon.vue +0 -33
  93. package/Svg.vue +0 -40
  94. package/components/_alert.scss +0 -5
  95. package/components/_basic.scss +0 -54
  96. package/components/_breadcrumbs.scss +0 -40
  97. package/components/_button.scss +0 -305
  98. package/components/_cascader.scss +0 -12
  99. package/components/_checkbox.scss +0 -40
  100. package/components/_collapse.scss +0 -25
  101. package/components/_datepicker.scss +0 -51
  102. package/components/_footer.scss +0 -46
  103. package/components/_form.scss +0 -22
  104. package/components/_header.scss +0 -55
  105. package/components/_icon.scss +0 -24
  106. package/components/_input.scss +0 -0
  107. package/components/_inputNumber.scss +0 -21
  108. package/components/_link.scss +0 -44
  109. package/components/_loader.scss +0 -44
  110. package/components/_menu.scss +0 -112
  111. package/components/_pageTitle.scss +0 -8
  112. package/components/_select.scss +0 -28
  113. package/components/_sidebar.scss +0 -57
  114. package/components/_switch.scss +0 -14
  115. package/components/_table.scss +0 -20
  116. package/components/_tooltip.scss +0 -4
  117. package/components/_typography.scss +0 -153
  118. package/components/_upload.scss +0 -15
  119. package/components/_wysiwyg.scss +0 -6
  120. package/components/_wysiwyg2.scss +0 -136
  121. package/form/Wysiwyg2.vue +0 -278
  122. package/layout/Footer.vue +0 -35
  123. package/layout/Loader.vue +0 -39
  124. package/modules/_AuthModule.scss +0 -209
  125. package/modules/_AuthVariables.scss +0 -7
  126. package/modules/_modal.scss +0 -24
  127. package/navigation/Menu.vue +0 -16
  128. package/navigation/MenuItem.vue +0 -20
  129. package/navigation/MenuItemGroup.vue +0 -20
  130. package/navigation/SubMenu.vue +0 -20
  131. /package/{form → _Build/vue/components/form}/Checkbox.vue +0 -0
  132. /package/{form → _Build/vue/components/form}/color.vue +0 -0
@@ -0,0 +1,169 @@
1
+ <!-- eslint-disable vue/no-mutating-props -->
2
+ <template>
3
+ <form class="" @submit.prevent="submit">
4
+ <template v-if="method !== 'patch'">
5
+ <el-basic
6
+ v-model="form.name"
7
+ name="name"
8
+ :error="form.errors"
9
+ type="text"
10
+ placeholder="Name"
11
+ label="Name"
12
+ />
13
+
14
+ <el-basic
15
+ v-model="form.email"
16
+ placeholder="Email"
17
+ label="Email"
18
+ type="text"
19
+ name="email"
20
+ :error="form.errors"
21
+ />
22
+
23
+ Company: <span v-text="company?.name" />
24
+
25
+ <hr class="my-3 hr-muted" />
26
+
27
+ <el-checkbox
28
+ v-model="form.notify_user"
29
+ name="notify_user"
30
+ :error="form.errors"
31
+ label="Send email to notify user of account and password"
32
+ />
33
+ <el-checkbox
34
+ v-model="form.force_password_change"
35
+ name="force_password_change"
36
+ :error="form.errors"
37
+ label="Force password change on first login"
38
+ />
39
+ <el-checkbox
40
+ v-model="form.set_password"
41
+ name="set_password"
42
+ :error="form.errors"
43
+ label="Auto generate password"
44
+ />
45
+ <template v-if="!form.set_password">
46
+ <el-basic
47
+ v-model="form.password"
48
+ name="password"
49
+ :error="form.errors"
50
+ type="password"
51
+ placeholder="Password"
52
+ label="Password"
53
+ />
54
+ <el-basic
55
+ v-model="form.password_confirmation"
56
+ name="password_confirmation"
57
+ :error="form.errors"
58
+ type="password"
59
+ placeholder="Password Confirmation"
60
+ label="Password Confirmation"
61
+ />
62
+ </template>
63
+
64
+ <hr class="my-5 hr-muted" />
65
+ </template>
66
+
67
+ <template v-if="$store.getters.can('edit roles')">
68
+ <template v-if="enableRoles">
69
+ <el-button
70
+ type="secondary"
71
+ @click="
72
+ form.roles = [];
73
+ enableRoles = false;
74
+ "
75
+ >
76
+ Reset roles
77
+ </el-button>
78
+ <FormRole :form="form" />
79
+ </template>
80
+ <template v-else>
81
+ <el-button @click="enableRoles = true">
82
+ <GIcon name="icon-plus" embed asis class="fill-0 mr" />
83
+ Override roles
84
+ </el-button>
85
+ <p class="mt-2">
86
+ Roles will be inherited from the company level.
87
+ </p>
88
+ </template>
89
+ <hr class="my-5 hr-muted" />
90
+ </template>
91
+
92
+ <VFormFooter :loading="form.processing" />
93
+ </form>
94
+ </template>
95
+
96
+ <script>
97
+ import axios from "axios";
98
+ import { debounce } from "lodash";
99
+
100
+ export default {
101
+ components: {
102
+ VFormFooter: require("../../../../components/VFormFooter.vue").default,
103
+ FormRole: require("../../../../components/FormRole.vue").default,
104
+ },
105
+ props: {
106
+ form: {
107
+ required: true,
108
+ type: Object,
109
+ },
110
+ submit: {
111
+ required: true,
112
+ type: Function,
113
+ },
114
+ method: {
115
+ required: true,
116
+ type: String,
117
+ },
118
+ },
119
+
120
+ data() {
121
+ return {
122
+ companies: null,
123
+ enableRoles: null,
124
+ company: null,
125
+ };
126
+ },
127
+
128
+ computed: {
129
+ domain() {
130
+ return this.form.email?.split("@")[1];
131
+ },
132
+ },
133
+
134
+ watch: {
135
+ "form.roles": {
136
+ handler(newVal) {
137
+ if (this.enableRoles === null) {
138
+ this.enableRoles = newVal.length > 0;
139
+ }
140
+ },
141
+ },
142
+ "form.email": debounce(function () {
143
+ if (this.domain) {
144
+ axios
145
+ .get("/api/companies", {
146
+ params: {
147
+ "filter[domains.domain]": this.domain,
148
+ },
149
+ })
150
+ .then((res) => {
151
+ this.company = res.data.data[0];
152
+ })
153
+ .catch((error) => {
154
+ console.error("Error fetching company:", error);
155
+ this.company = null;
156
+ });
157
+ } else {
158
+ this.company = null;
159
+ }
160
+ }, 500),
161
+ },
162
+
163
+ mounted() {
164
+ axios.getAll("/api/companies").then((res) => {
165
+ this.companies = res.data.data;
166
+ });
167
+ },
168
+ };
169
+ </script>
@@ -0,0 +1,120 @@
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="user">
11
+ <div class="bg-0 p-3 box-shadow-1 border-r-4 mb-6">
12
+ <VPageHeader
13
+ icon="icon-user"
14
+ :title="`${user.name} ${user.last_name ?? ''}`"
15
+ >
16
+ <el-button
17
+ v-if="$store.getters.can('write users')"
18
+ tag="a"
19
+ type="primary"
20
+ @click="
21
+ $router.push({
22
+ name: 'users.edit',
23
+ param: user.id,
24
+ })
25
+ "
26
+ >
27
+ <GIcon
28
+ class="fill-0 mr-0.5"
29
+ name="icon-edit"
30
+ embed
31
+ artboard
32
+ />
33
+ Edit user
34
+ </el-button>
35
+ </VPageHeader>
36
+
37
+ <hr class="my-3 hr-muted" />
38
+
39
+ <table>
40
+ <tbody>
41
+ <tr>
42
+ <td class="p">Email</td>
43
+ <td class="p">{{ user.email }}</td>
44
+ </tr>
45
+ <tr>
46
+ <td class="p">Company</td>
47
+ <td class="p">
48
+ <router-link
49
+ :to="{
50
+ name: 'companies.show',
51
+ params: { id: user.company.id },
52
+ }"
53
+ >
54
+ {{ user.company?.name }}
55
+ </router-link>
56
+ </td>
57
+ </tr>
58
+ </tbody>
59
+ </table>
60
+
61
+ <hr class="my-3 hr-muted" />
62
+
63
+ <FormRole
64
+ :overrides="user.overrides_roles_and_permissions"
65
+ :form="{ roles: user.roles.map((d) => d.id) }"
66
+ :readonly="true"
67
+ />
68
+ </div>
69
+ </template>
70
+
71
+ <div v-else class="absolute transform-center text-center">
72
+ <GSpinner class="fill-5" />
73
+ <p v-text="`Loading...`" />
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </template>
78
+
79
+ <script>
80
+ import axios from "axios";
81
+
82
+ export default {
83
+ name: "PShow",
84
+
85
+ components: {
86
+ VBreadcrumbs: require("../../../components/VBreadcrumbs.vue").default,
87
+ VPageHeader: require("../../../components/VPageHeader.vue").default,
88
+ FormRole: require("../../../components/FormRole.vue").default,
89
+ },
90
+
91
+ props: {
92
+ breadcrumbs: {
93
+ type: Array,
94
+ required: true,
95
+ },
96
+ },
97
+
98
+ data() {
99
+ return {
100
+ user: null,
101
+ addBreadcrumbs: [...this.$props.breadcrumbs],
102
+ };
103
+ },
104
+
105
+ mounted() {
106
+ axios
107
+ .get(`/api/users/${this.$route.params.id}?include=company`)
108
+ .then((res) => {
109
+ this.user = res.data.data;
110
+ this.addBreadcrumbs.push({
111
+ href: {
112
+ name: "users.show",
113
+ param: this.user.id,
114
+ },
115
+ text: this.user.name,
116
+ });
117
+ });
118
+ },
119
+ };
120
+ </script>
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <PageTitle title="Users" />
3
+
4
+ <router-view :key="$route.path" :breadcrumbs="breadcrumbs" />
5
+ </template>
6
+
7
+ <script>
8
+ import { PageTitle } from "@fishawack/lab-velocity";
9
+
10
+ export default {
11
+ name: "PUsers",
12
+
13
+ components: {
14
+ PageTitle,
15
+ },
16
+
17
+ data() {
18
+ return {
19
+ breadcrumbs: [
20
+ {
21
+ href: {
22
+ name: "index",
23
+ },
24
+ text: "Home",
25
+ },
26
+ {
27
+ href: {
28
+ name: "users.index",
29
+ },
30
+ text: "Users",
31
+ },
32
+ ],
33
+ };
34
+ },
35
+ };
36
+ </script>
@@ -1,19 +1,15 @@
1
1
  <template>
2
- <div :class="{'active': open}"
3
- class="modal modal--auth">
4
-
5
- <div class="modal__container"
6
- v-on:click.self="close">
7
-
2
+ <div :class="{ active: open }" class="modal modal--auth">
3
+ <div class="modal__container" v-on:click.self="close">
8
4
  <div class="modal__box AuthModule__form" :class="boxCls">
9
- <component v-if="compName"
10
- :is="compName"
11
- v-bind="compProps"
12
- @close="close"
13
- />
5
+ <component
6
+ v-if="compName"
7
+ :is="compName"
8
+ v-bind="compProps"
9
+ @close="close"
10
+ />
14
11
  </div>
15
12
  </div>
16
-
17
13
  </div>
18
14
  </template>
19
15
 
@@ -31,39 +27,38 @@ export default {
31
27
  cb: null,
32
28
  boxCls: null,
33
29
  validComponents: {
34
- "force-reset" : 'VForceReset',
35
- "password-change" : 'VPasswordChange'
36
- }
30
+ "force-reset": "VForceReset",
31
+ "password-change": "VPasswordChange",
32
+ },
37
33
  };
38
34
  },
39
35
 
40
36
  methods: {
41
- reset(){
37
+ reset() {
42
38
  this.compName = null;
43
39
  this.compProps = null;
44
40
  this.boxCls = null;
45
41
  this.cb = null;
46
42
  },
47
43
  close() {
48
- if(this.compName === 'VForceReset') {
49
- if(!this.$store.state.auth.forcePasswordChange) {
44
+ if (this.compName === "VForceReset") {
45
+ if (!this.$store.state.auth.forcePasswordChange) {
50
46
  this.open = false;
51
47
  }
52
- } else {
48
+ } else {
53
49
  this.open = false;
54
50
  }
55
- }
51
+ },
56
52
  },
57
53
 
58
54
  created() {
59
- this.$emitter.on('AuthModal', (comp) => {
55
+ this.$emitter.on("AuthModal", (comp) => {
60
56
  this.compName = this.validComponents[comp.type];
61
57
  this.open = comp;
62
-
63
58
  });
64
59
 
65
- window.addEventListener('keydown', (e) => {
66
- if (e.key === 'Escape') {
60
+ window.addEventListener("keydown", (e) => {
61
+ if (e.key === "Escape") {
67
62
  this.open = false;
68
63
  }
69
64
  });
@@ -71,9 +66,9 @@ export default {
71
66
 
72
67
  watch: {
73
68
  open(isOpen) {
74
- this.$emitter.emit('toggleBackgroundScroll', isOpen);
69
+ this.$emitter.emit("toggleBackgroundScroll", isOpen);
75
70
 
76
- if(this.cb){
71
+ if (this.cb) {
77
72
  this.cb();
78
73
  }
79
74
 
@@ -81,19 +76,19 @@ export default {
81
76
  this.reset();
82
77
  }
83
78
  },
84
- '$route'(to,from) {
85
- if(from.fullPath !== to.fullPath) {
79
+ $route(to, from) {
80
+ if (from.fullPath !== to.fullPath) {
86
81
  this.open = false;
87
82
  }
88
83
  },
89
- '$store.state.auth.forcePasswordChange': {
84
+ "$store.state.auth.forcePasswordChange": {
90
85
  async handler(forcePasswordChange) {
91
- if(forcePasswordChange) {
86
+ if (forcePasswordChange) {
92
87
  this.compName = this.validComponents["force-reset"];
93
88
  this.open = true;
94
89
  }
95
- }
96
- }
90
+ },
91
+ },
97
92
  },
98
93
 
99
94
  components: {
@@ -101,10 +96,10 @@ export default {
101
96
  VPasswordChange: require("../routes/change-password.vue").default,
102
97
  },
103
98
  mounted() {
104
- if(this.$store.state.auth.forcePasswordChange) {
99
+ if (this.$store.state.auth.forcePasswordChange) {
105
100
  this.compName = this.validComponents["force-reset"];
106
101
  this.open = true;
107
102
  }
108
- }
103
+ },
109
104
  };
110
- </script>
105
+ </script>
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <span
3
+ class="chip"
4
+ :class="`chip--${name}`"
5
+ :style="{
6
+ color,
7
+ }"
8
+ >
9
+ {{ label }}
10
+ </span>
11
+ </template>
12
+
13
+ <script>
14
+ export default {
15
+ props: {
16
+ name: {
17
+ type: String,
18
+ default: "",
19
+ },
20
+ label: {
21
+ type: String,
22
+ default: "",
23
+ },
24
+ },
25
+
26
+ computed: {
27
+ color() {
28
+ return this.generateColorFromString(this.name);
29
+ },
30
+ },
31
+
32
+ methods: {
33
+ /**
34
+ * Generate a more visually appealing color with better saturation
35
+ */
36
+ generateColorFromString(str) {
37
+ let hash = 0;
38
+
39
+ // Create hash from string
40
+ for (let i = 0; i < str.length; i++) {
41
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
42
+ }
43
+
44
+ // Generate HSL values for better color distribution
45
+ const hue = Math.abs(hash % 360);
46
+ const saturation = 45 + (Math.abs(hash) % 30); // 45-75%
47
+ const lightness = 35 + (Math.abs(hash) % 25); // 35-60%
48
+
49
+ return this.hslToHex(hue, saturation, lightness);
50
+ },
51
+
52
+ /**
53
+ * Convert HSL to HEX
54
+ */
55
+ hslToHex(h, s, l) {
56
+ // eslint-disable-next-line no-param-reassign
57
+ l /= 100;
58
+ const a = (s * Math.min(l, 1 - l)) / 100;
59
+ const f = (n) => {
60
+ const k = (n + h / 30) % 12;
61
+ const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
62
+ return Math.round(255 * color)
63
+ .toString(16)
64
+ .padStart(2, "0");
65
+ };
66
+ return `#${f(0)}${f(8)}${f(4)}`;
67
+ },
68
+ },
69
+ };
70
+ </script>
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <div class="chips">
3
+ <template v-for="(item, index) in array" :key="item.name">
4
+ <Chip
5
+ class="chip--round"
6
+ :name="item.name"
7
+ :label="`${item.label}${index < array.length - 1 ? ',' : ''}`"
8
+ /><span v-if="index < array.length - 1">&nbsp;</span>
9
+ </template>
10
+ </div>
11
+ </template>
12
+
13
+ <script>
14
+ export default {
15
+ components: {
16
+ Chip: require("./Chip.vue").default,
17
+ },
18
+
19
+ props: {
20
+ array: {
21
+ type: Array,
22
+ default: () => [],
23
+ },
24
+ },
25
+ };
26
+ </script>
@@ -0,0 +1,115 @@
1
+ <!-- eslint-disable vue/no-mutating-props -->
2
+ <template>
3
+ <h2 class="font-primary">Access Control</h2>
4
+
5
+ <template v-if="readonly">
6
+ <p>Roles</p>
7
+ <div v-if="!overrides" class="my-2">
8
+ <Chip
9
+ class="mr mb inline-block"
10
+ name="inherited"
11
+ label="Inherited"
12
+ />
13
+ </div>
14
+ <ul v-else class="my-2 pl-0">
15
+ <li
16
+ v-for="role in form.roles"
17
+ :key="roles.find((d) => d.id === role)"
18
+ class="mr mb inline-block"
19
+ >
20
+ <Chip
21
+ :name="roles.find((d) => d.id === role)?.name"
22
+ :label="roles.find((d) => d.id === role)?.label"
23
+ />
24
+ </li>
25
+ </ul>
26
+ </template>
27
+ <el-select
28
+ v-else
29
+ v-model="form.roles"
30
+ class="mt-2"
31
+ placeholder="Select"
32
+ multiple
33
+ name="roles"
34
+ :error="form.errors"
35
+ label="Roles"
36
+ :options="
37
+ roles?.map(({ id, label }) => ({
38
+ label,
39
+ value: id,
40
+ }))
41
+ "
42
+ />
43
+
44
+ <template v-if="permissions.length && (overrides === null || overrides)">
45
+ <p>Permissions</p>
46
+ <ul class="mt-2 pl-0">
47
+ <li
48
+ v-for="(permission, index) in permissions"
49
+ :key="index"
50
+ class="mr mb inline-block"
51
+ >
52
+ <Chip :name="permission.name" :label="permission.label" />
53
+ </li>
54
+ </ul>
55
+ </template>
56
+
57
+ <VRoleLegend class="mt-2" />
58
+ </template>
59
+
60
+ <script>
61
+ import axios from "axios";
62
+
63
+ export default {
64
+ components: {
65
+ VRoleLegend: require("./VRoleLegend.vue").default,
66
+ Chip: require("./Chip.vue").default,
67
+ },
68
+
69
+ props: {
70
+ form: {
71
+ required: true,
72
+ type: Object,
73
+ },
74
+ readonly: {
75
+ type: Boolean,
76
+ default: false,
77
+ },
78
+ overrides: {
79
+ type: Boolean,
80
+ default: null,
81
+ },
82
+ },
83
+
84
+ data() {
85
+ return {
86
+ roles: [],
87
+ };
88
+ },
89
+
90
+ computed: {
91
+ permissions() {
92
+ const allPermissions = this.form.roles.reduce((acc, id) => {
93
+ return acc.concat(
94
+ this.roles.find((d) => d.id === id)?.permissions || [],
95
+ );
96
+ }, []);
97
+
98
+ // Remove duplicates based on permission id
99
+ return allPermissions
100
+ .filter((permission, index, self) => {
101
+ return (
102
+ index === self.findIndex((p) => p.id === permission.id)
103
+ );
104
+ })
105
+ .sort();
106
+ },
107
+ },
108
+
109
+ mounted() {
110
+ axios.getAll("/api/roles").then((res) => {
111
+ this.roles = res.data.data;
112
+ });
113
+ },
114
+ };
115
+ </script>