@fishawack/lab-velocity 2.0.0-beta.4 → 2.0.0-beta.41

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 (110) hide show
  1. package/README.md +441 -37
  2. package/_Build/vue/components/basic/Button.vue +1 -1
  3. package/_Build/vue/components/form/Checkbox.vue +10 -0
  4. package/_Build/vue/components/form/Select.vue +223 -33
  5. package/_Build/vue/components/form/Spinner.vue +5 -0
  6. package/_Build/vue/components/layout/Alert.vue +5 -5
  7. package/_Build/vue/components/layout/Audit.vue +75 -0
  8. package/_Build/vue/{modules/AuthModule/components/VBreadcrumbs.vue → components/layout/Breadcrumbs.vue} +4 -4
  9. package/_Build/vue/{modules/AuthModule/components → components/layout}/Chips.vue +2 -2
  10. package/_Build/vue/components/layout/Footer.vue +11 -10
  11. package/_Build/vue/{modules/AuthModule/components/VFormFooter.vue → components/layout/FormFooter.vue} +13 -7
  12. package/_Build/vue/{modules/AuthModule/components → components/layout}/FormRole.vue +10 -8
  13. package/_Build/vue/components/layout/Layout.vue +76 -0
  14. package/_Build/vue/components/layout/Navigation.vue +77 -0
  15. package/_Build/vue/{modules/AuthModule/components/VPageHeader.vue → components/layout/PageHeader.vue} +7 -2
  16. package/_Build/vue/components/layout/SideBar.vue +26 -0
  17. package/_Build/vue/{modules/AuthModule/components/VTable.vue → components/layout/Table.vue} +32 -16
  18. package/_Build/vue/{modules/AuthModule/components/VTableSorter.vue → components/layout/TableSorter.vue} +68 -43
  19. package/_Build/vue/components/layout/pageTitle.vue +1 -1
  20. package/_Build/vue/components/navigation/MenuItem.vue +7 -2
  21. package/_Build/vue/components/navigation/MenuItemGroup.vue +7 -2
  22. package/_Build/vue/modules/AuthModule/js/axios.js +19 -0
  23. package/_Build/vue/modules/AuthModule/js/router.js +24 -89
  24. package/_Build/vue/modules/AuthModule/js/store.js +15 -6
  25. package/_Build/vue/modules/AuthModule/routes/PCompanies/columns.js +268 -0
  26. package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +213 -0
  27. package/_Build/vue/modules/AuthModule/routes/PTeams/resource.js +334 -0
  28. package/_Build/vue/modules/AuthModule/routes/PUsers/columns.js +349 -0
  29. package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +203 -0
  30. package/_Build/vue/modules/AuthModule/routes/account-exists.vue +2 -2
  31. package/_Build/vue/modules/AuthModule/routes/change-password.vue +23 -24
  32. package/_Build/vue/modules/AuthModule/routes/container.vue +2 -11
  33. package/_Build/vue/modules/AuthModule/routes/expired-reset.vue +4 -4
  34. package/_Build/vue/modules/AuthModule/routes/expired-verification.vue +9 -8
  35. package/_Build/vue/modules/AuthModule/routes/force-reset.vue +39 -50
  36. package/_Build/vue/modules/AuthModule/routes/forgot.vue +4 -4
  37. package/_Build/vue/modules/AuthModule/routes/login.vue +7 -11
  38. package/_Build/vue/modules/AuthModule/routes/logincallback.vue +1 -3
  39. package/_Build/vue/modules/AuthModule/routes/loginsso.vue +7 -9
  40. package/_Build/vue/modules/AuthModule/routes/logout.vue +1 -3
  41. package/_Build/vue/modules/AuthModule/routes/logoutheadless.vue +1 -3
  42. package/_Build/vue/modules/AuthModule/routes/register.vue +19 -21
  43. package/_Build/vue/modules/AuthModule/routes/reset.vue +14 -13
  44. package/_Build/vue/modules/AuthModule/routes/success-forgot.vue +8 -7
  45. package/_Build/vue/modules/AuthModule/routes/success-reset.vue +2 -2
  46. package/_Build/vue/modules/AuthModule/routes/success-verify.vue +1 -3
  47. package/_Build/vue/modules/AuthModule/routes/verify.vue +11 -14
  48. package/_Build/vue/modules/resource/Children/create.vue +81 -0
  49. package/_Build/vue/modules/resource/Children/edit.vue +106 -0
  50. package/_Build/vue/modules/resource/Children/index.vue +42 -0
  51. package/_Build/vue/modules/resource/Children/partials/form.vue +61 -0
  52. package/_Build/vue/modules/resource/Children/show.vue +144 -0
  53. package/_Build/vue/modules/resource/index.js +545 -0
  54. package/_Build/vue/modules/resource/parent.vue +63 -0
  55. package/_base.scss +0 -1
  56. package/_defaults.scss +2 -13
  57. package/_variables.scss +9 -4
  58. package/components/_alert.scss +5 -0
  59. package/components/_auth.scss +163 -0
  60. package/components/_basic.scss +55 -0
  61. package/components/_breadcrumbs.scss +39 -0
  62. package/components/_button.scss +304 -0
  63. package/components/_cascader.scss +12 -0
  64. package/components/_checkbox.scss +41 -0
  65. package/components/_chip.scss +24 -0
  66. package/components/_collapse.scss +24 -0
  67. package/components/_datepicker.scss +53 -0
  68. package/components/_descriptions.scss +2 -0
  69. package/components/_footer.scss +47 -0
  70. package/components/_form.scss +24 -0
  71. package/components/_header.scss +30 -0
  72. package/components/_icon.scss +25 -0
  73. package/components/_inputNumber.scss +22 -0
  74. package/components/_layout.scss +56 -0
  75. package/components/_link.scss +44 -0
  76. package/components/_loader.scss +43 -0
  77. package/components/_menu.scss +112 -0
  78. package/components/_modal.scss +24 -0
  79. package/components/_pageTitle.scss +8 -0
  80. package/components/_permissionLegend.scss +18 -0
  81. package/components/_select.scss +29 -0
  82. package/components/_sidebar.scss +41 -0
  83. package/components/_switch.scss +14 -0
  84. package/components/_table.scss +20 -0
  85. package/components/_tooltip.scss +4 -0
  86. package/components/_typography.scss +162 -0
  87. package/components/_upload.scss +15 -0
  88. package/components/_wysiwyg.scss +7 -0
  89. package/components/_wysiwyg2.scss +142 -0
  90. package/index.js +16 -1
  91. package/package.json +5 -3
  92. package/vendor.scss +0 -1
  93. package/_Build/vue/components/layout/sideBar.vue +0 -25
  94. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/Upload/upload.vue +0 -251
  95. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/create.vue +0 -62
  96. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/edit.vue +0 -98
  97. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/index.vue +0 -90
  98. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/partials/form.vue +0 -173
  99. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/show.vue +0 -262
  100. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/parent.vue +0 -36
  101. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/create.vue +0 -112
  102. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/edit.vue +0 -103
  103. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/index.vue +0 -112
  104. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/partials/form.vue +0 -169
  105. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/show.vue +0 -120
  106. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/parent.vue +0 -36
  107. /package/_Build/vue/{modules/AuthModule/components → components/layout}/AuthModal.vue +0 -0
  108. /package/_Build/vue/{modules/AuthModule/components → components/layout}/Chip.vue +0 -0
  109. /package/_Build/vue/{modules/AuthModule/components/VPasswordValidation.vue → components/layout/PasswordValidation.vue} +0 -0
  110. /package/_Build/vue/{modules/AuthModule/components/VRoleLegend.vue → components/layout/RoleLegend.vue} +0 -0
@@ -0,0 +1,334 @@
1
+ import { merge } from "lodash";
2
+ import { h, resolveComponent } from "vue";
3
+ import axios from "axios";
4
+ import { throttle } from "lodash";
5
+
6
+ import { columns } from "../../../resource/index.js";
7
+ import userResource from "../PUsers/resource.js";
8
+ import { defaultResource, meta } from "../../../resource/index.js";
9
+
10
+ import VelFormRole from "../../../../components/layout/FormRole.vue";
11
+ import Chip from "../../../../components/layout/Chip.vue";
12
+ import Chips from "../../../../components/layout/Chips.vue";
13
+ import VelRoleLegend from "../../../../components/layout/RoleLegend.vue";
14
+
15
+ import VelTableSorter from "../../../../components/layout/TableSorter.vue";
16
+ import VelButton from "../../../../components/basic/Button.vue";
17
+ import VelCheckbox from "../../../../components/form/Checkbox.vue";
18
+ import VelSelect from "../../../../components/form/Select.vue";
19
+
20
+ export default [
21
+ "teams",
22
+ {
23
+ icon: `icon-account-circle`,
24
+ api: {
25
+ params: {
26
+ index: ({ $route }) => ({
27
+ include: "company",
28
+ "filter[company_id]": $route.params.companiesId,
29
+ }),
30
+ show: () => ({
31
+ include: "company",
32
+ }),
33
+ },
34
+ },
35
+ permissions: {
36
+ create: ({ $store }) => $store.getters.can("write teams"),
37
+ edit: ({ $store }) => $store.getters.can("write teams"),
38
+ delete: ({ $store }) => $store.getters.can("delete teams"),
39
+ },
40
+ ...merge(
41
+ columns([
42
+ {
43
+ key: "name",
44
+ sortable: true,
45
+ },
46
+ {
47
+ key: "description",
48
+ },
49
+ {
50
+ key: "company_id",
51
+ label: "Company",
52
+ endpoint: "api/companies",
53
+ labelKey: "name",
54
+ filterable: true,
55
+ searchParam: "name",
56
+ condition: {
57
+ form: {
58
+ write: ({ $route, model }) =>
59
+ !$route.params.companiesId && !model?.company,
60
+ },
61
+ },
62
+ initial: ({ model, $route }) =>
63
+ model?.company ||
64
+ ($route.params.companiesId && {
65
+ id: $route.params.companiesId,
66
+ }),
67
+ preparation: ({ form }) => form.company_id?.id,
68
+ render: {
69
+ read: ({ model }) => h("span", model.company.name),
70
+ write: () => h(VelSelect),
71
+ },
72
+ },
73
+ {
74
+ key: "user_count",
75
+ label: "Users",
76
+ condition: {
77
+ form: false,
78
+ },
79
+ },
80
+ {
81
+ key: "roles",
82
+ initial: ({ model }) =>
83
+ model?.roles.map((val) => val.id) || [],
84
+ preparation: ({ form }) =>
85
+ form.roles?.map((d) =>
86
+ typeof d === "object" ? d.value : d,
87
+ ),
88
+ render: {
89
+ read: ({ model }) =>
90
+ h(
91
+ !model.overrides_roles_and_permissions ||
92
+ model.roles.length === 1
93
+ ? Chip
94
+ : Chips,
95
+ !model.overrides_roles_and_permissions
96
+ ? {
97
+ name: "inherited",
98
+ label: "Inherited",
99
+ }
100
+ : model.roles.length === 1
101
+ ? {
102
+ name: model.roles[0].name,
103
+ label: model.roles[0].label,
104
+ }
105
+ : { array: model.roles },
106
+ ),
107
+ write: ({ model, form }) =>
108
+ h(VelFormRole, {
109
+ overrides:
110
+ model?.overrides_roles_and_permissions,
111
+ form,
112
+ }),
113
+ },
114
+ },
115
+ ]),
116
+ {
117
+ index: {
118
+ layout: [
119
+ ...defaultResource.index.layout,
120
+ () =>
121
+ h(VelRoleLegend, {
122
+ class: "mt-5",
123
+ }),
124
+ ],
125
+ },
126
+ show: {
127
+ tabs: [
128
+ ...defaultResource.show.tabs,
129
+ ({ model }) => ({
130
+ label: "Access control",
131
+ component: h(VelFormRole, {
132
+ overrides:
133
+ model.overrides_roles_and_permissions,
134
+ form: { roles: model.roles.map((d) => d.id) },
135
+ readonly: true,
136
+ }),
137
+ }),
138
+ (props) => {
139
+ const { model, $store, $router, $route, ...rest } =
140
+ props;
141
+
142
+ return {
143
+ label: "Members",
144
+ component: h({
145
+ data: () => ({
146
+ scoped: true,
147
+ }),
148
+ mounted() {
149
+ this.emitter.on("reload-teams", () => {
150
+ this.reload();
151
+ });
152
+ },
153
+ beforeUnmount() {
154
+ this.emitter.off("reload-teams");
155
+ },
156
+ methods: {
157
+ reload: throttle(function () {
158
+ this.$refs.members.reload();
159
+ this.$refs.users.reload();
160
+ }, 1000),
161
+ },
162
+ render() {
163
+ return h("div", [
164
+ h("h3", "Members"),
165
+ (() => {
166
+ const resource = meta(
167
+ ...userResource,
168
+ );
169
+
170
+ resource.api.params.index = ({
171
+ $route,
172
+ }) => ({
173
+ include: "company",
174
+ "filter[teams.id]":
175
+ $route.params.teamsId,
176
+ });
177
+
178
+ resource.table.actions = [
179
+ ({ model }) =>
180
+ h({
181
+ data: () => ({
182
+ loading: false,
183
+ }),
184
+ render() {
185
+ return h(
186
+ VelButton,
187
+ {
188
+ tag: "a",
189
+ size: "small",
190
+ type: "warning",
191
+ loading:
192
+ this
193
+ .loading,
194
+ onClick:
195
+ async () => {
196
+ this.loading = true;
197
+
198
+ await axios.delete(
199
+ `api/teams/${$route.params.teamsId}/users/${model.id}`,
200
+ );
201
+
202
+ this.emitter.emit(
203
+ "reload-teams",
204
+ );
205
+ },
206
+ },
207
+ "Remove",
208
+ );
209
+ },
210
+ }),
211
+ ];
212
+
213
+ const props = {
214
+ model,
215
+ $store,
216
+ $router,
217
+ $route,
218
+ ...rest,
219
+ resource,
220
+ };
221
+
222
+ return h(
223
+ VelTableSorter,
224
+ merge(
225
+ resource.index.structure(
226
+ props,
227
+ ),
228
+ {
229
+ ref: "members",
230
+ },
231
+ ),
232
+ );
233
+ })(),
234
+ h("h3", "Users"),
235
+ h(VelCheckbox, {
236
+ label: "Only show users from this company",
237
+ class: "mt-2",
238
+ modelValue: this.scoped,
239
+ "onUpdate:modelValue": (
240
+ value,
241
+ ) => {
242
+ this.scoped = value;
243
+ this.$nextTick(() => {
244
+ this.$refs.users.reload();
245
+ });
246
+ },
247
+ }),
248
+ (() => {
249
+ const resource = meta(
250
+ ...userResource,
251
+ );
252
+
253
+ resource.api.params.index = ({
254
+ $route,
255
+ }) => ({
256
+ include: "company",
257
+ "filter[company_id]": this
258
+ .scoped
259
+ ? $route.params
260
+ .companiesId
261
+ : null,
262
+ "filter[teams.id]": `!${$route.params.teamsId}`,
263
+ });
264
+
265
+ resource.table.actions = [
266
+ ({ model }) =>
267
+ h({
268
+ data: () => ({
269
+ loading: false,
270
+ }),
271
+ render() {
272
+ return h(
273
+ VelButton,
274
+ {
275
+ tag: "a",
276
+ size: "small",
277
+ type: "primary",
278
+ loading:
279
+ this
280
+ .loading,
281
+ onClick:
282
+ async () => {
283
+ this.loading = true;
284
+
285
+ await axios.post(
286
+ `api/teams/${$route.params.teamsId}/users`,
287
+ {
288
+ id: model.id,
289
+ },
290
+ );
291
+
292
+ this.emitter.emit(
293
+ "reload-teams",
294
+ );
295
+ },
296
+ },
297
+ "Add",
298
+ );
299
+ },
300
+ }),
301
+ ];
302
+
303
+ const props = {
304
+ model,
305
+ $store,
306
+ $router,
307
+ $route,
308
+ ...rest,
309
+ resource,
310
+ };
311
+
312
+ return h(
313
+ VelTableSorter,
314
+ merge(
315
+ resource.index.structure(
316
+ props,
317
+ ),
318
+ {
319
+ ref: "users",
320
+ },
321
+ ),
322
+ );
323
+ })(),
324
+ ]);
325
+ },
326
+ }),
327
+ };
328
+ },
329
+ ],
330
+ },
331
+ },
332
+ ),
333
+ },
334
+ ];
@@ -0,0 +1,349 @@
1
+ import { h, resolveComponent, ref } from "vue";
2
+ import { debounce } from "lodash";
3
+ import axios from "axios";
4
+
5
+ import VelBasic from "../../../../components/form/basic.vue";
6
+ import VelButton from "../../../../components/basic/Button.vue";
7
+ import VelCheckbox from "../../../../components/form/Checkbox.vue";
8
+ import VelFormRole from "../../../../components/layout/FormRole.vue";
9
+ import Chip from "../../../../components/layout/Chip.vue";
10
+ import Chips from "../../../../components/layout/Chips.vue";
11
+
12
+ import companyResource from "../PCompanies/resource.js";
13
+ import { meta } from "../../../resource/index.js";
14
+
15
+ /**
16
+ * Shared reactive state for SSO company detection.
17
+ * Used by the email column's render.write and by form.submit in resource.js.
18
+ */
19
+ export const detectedCompany = ref(null);
20
+
21
+ const debouncedFetchCompany = debounce((domain) => {
22
+ if (domain) {
23
+ axios
24
+ .get("/api/companies", {
25
+ params: {
26
+ "filter[domains.domain]": domain,
27
+ },
28
+ })
29
+ .then((res) => {
30
+ detectedCompany.value = res.data.data[0] || null;
31
+ })
32
+ .catch(() => {
33
+ detectedCompany.value = null;
34
+ });
35
+ } else {
36
+ detectedCompany.value = null;
37
+ }
38
+ }, 500);
39
+
40
+ /**
41
+ * Hoisted inline component for the email column's render.write.
42
+ * Must be a stable reference so Vue doesn't remount on every render.
43
+ */
44
+ const EmailField = {
45
+ props: ["form"],
46
+ data() {
47
+ return {
48
+ company: detectedCompany.value,
49
+ };
50
+ },
51
+ watch: {
52
+ "form.email": {
53
+ handler(val) {
54
+ const domain = val?.split("@")[1];
55
+ debouncedFetchCompany(domain);
56
+ },
57
+ },
58
+ },
59
+ created() {
60
+ this.$watch(
61
+ () => detectedCompany.value,
62
+ (val) => {
63
+ this.company = val;
64
+ },
65
+ );
66
+ },
67
+ render() {
68
+ const form = this.form;
69
+ const company = this.company;
70
+ const isSSOCompany = !!company?.sso_enabled;
71
+
72
+ return h("div", [
73
+ h(VelBasic, {
74
+ modelValue: form.email,
75
+ "onUpdate:modelValue": (val) => {
76
+ form.email = val;
77
+ },
78
+ placeholder: "Email",
79
+ label: "Email",
80
+ type: "text",
81
+ name: "email",
82
+ error: form.errors,
83
+ }),
84
+ h("p", { class: "mb-1" }, ["Company: ", h("span", company?.name)]),
85
+ h("hr", { class: "my-3 hr-muted" }),
86
+ isSSOCompany
87
+ ? h("p", { class: "mb-3" }, [
88
+ "This user belongs to an SSO-enabled company. They will authenticate via SSO on first login. No password is required.",
89
+ ])
90
+ : h("div", [
91
+ h(VelCheckbox, {
92
+ modelValue: form.notify_user,
93
+ "onUpdate:modelValue": (val) => {
94
+ form.notify_user = val;
95
+ },
96
+ name: "notify_user",
97
+ error: form.errors,
98
+ label: "Send email to notify user of account and password",
99
+ }),
100
+ h(VelCheckbox, {
101
+ modelValue: form.force_password_change,
102
+ "onUpdate:modelValue": (val) => {
103
+ form.force_password_change = val;
104
+ },
105
+ name: "force_password_change",
106
+ error: form.errors,
107
+ label: "Force password change on first login",
108
+ }),
109
+ h(VelCheckbox, {
110
+ modelValue: form.set_password,
111
+ "onUpdate:modelValue": (val) => {
112
+ form.set_password = val;
113
+ },
114
+ name: "set_password",
115
+ error: form.errors,
116
+ label: "Auto generate password",
117
+ }),
118
+ !form.set_password &&
119
+ h("div", [
120
+ h(VelBasic, {
121
+ modelValue: form.password,
122
+ "onUpdate:modelValue": (val) => {
123
+ form.password = val;
124
+ },
125
+ name: "password",
126
+ error: form.errors,
127
+ type: "password",
128
+ placeholder: "Password",
129
+ label: "Password",
130
+ }),
131
+ h(VelBasic, {
132
+ modelValue: form.password_confirmation,
133
+ "onUpdate:modelValue": (val) => {
134
+ form.password_confirmation = val;
135
+ },
136
+ name: "password_confirmation",
137
+ error: form.errors,
138
+ type: "password",
139
+ placeholder: "Password Confirmation",
140
+ label: "Password Confirmation",
141
+ }),
142
+ ]),
143
+ ]),
144
+ h("hr", { class: "my-5 hr-muted" }),
145
+ ]);
146
+ },
147
+ };
148
+
149
+ /**
150
+ * Hoisted inline component for the roles column's render.write.
151
+ * Must be a stable reference so Vue doesn't remount on every render.
152
+ */
153
+ const RolesField = {
154
+ props: ["form"],
155
+ data() {
156
+ return {
157
+ enableRoles: null,
158
+ };
159
+ },
160
+ watch: {
161
+ "form.roles": {
162
+ immediate: true,
163
+ handler(newVal) {
164
+ if (this.enableRoles === null) {
165
+ this.enableRoles = newVal && newVal.length > 0;
166
+ }
167
+ },
168
+ },
169
+ },
170
+ render() {
171
+ const form = this.form;
172
+
173
+ if (this.enableRoles) {
174
+ return h("div", [
175
+ h(
176
+ VelButton,
177
+ {
178
+ onClick: () => {
179
+ form.roles = [];
180
+ this.enableRoles = false;
181
+ },
182
+ },
183
+ "Reset roles",
184
+ ),
185
+ h(VelFormRole, { form }),
186
+ h("hr", { class: "my-5 hr-muted" }),
187
+ ]);
188
+ }
189
+
190
+ return h("div", [
191
+ h(
192
+ VelButton,
193
+ {
194
+ onClick: () => {
195
+ this.enableRoles = true;
196
+ },
197
+ },
198
+ () => [
199
+ h(resolveComponent("GIcon"), {
200
+ name: "icon-plus",
201
+ embed: true,
202
+ asis: true,
203
+ class: "fill-0 icon--0.5 mr",
204
+ }),
205
+ "Override roles",
206
+ ],
207
+ ),
208
+ h("p", { class: "mt-2" }, [
209
+ "Roles will be inherited from the company level.",
210
+ ]),
211
+ h("hr", { class: "my-5 hr-muted" }),
212
+ ]);
213
+ },
214
+ };
215
+
216
+ export default [
217
+ {
218
+ key: "name",
219
+ sortable: true,
220
+ condition: {
221
+ form: {
222
+ write: ({ method }) => method === "post",
223
+ },
224
+ },
225
+ },
226
+ {
227
+ key: "email",
228
+ condition: {
229
+ form: {
230
+ write: ({ method }) => method === "post",
231
+ },
232
+ },
233
+ render: {
234
+ write: ({ form }) => h(EmailField, { form }),
235
+ },
236
+ initial: ({ model }) => model?.email ?? null,
237
+ },
238
+ {
239
+ key: "company",
240
+ sortable: true,
241
+ condition: {
242
+ form: false,
243
+ },
244
+ render: {
245
+ read: ({ model }) =>
246
+ !model.company
247
+ ? h("span", "")
248
+ : model.company.deleted_at
249
+ ? h(
250
+ "span",
251
+ { class: "vel-basic__error" },
252
+ model.company.name,
253
+ )
254
+ : h(resolveComponent("router-link"), {
255
+ class: "underline",
256
+ to: {
257
+ name: "companies.show",
258
+ params: {
259
+ [meta(...companyResource).id]:
260
+ model.company_id,
261
+ },
262
+ },
263
+ text: model.company.name,
264
+ }),
265
+ },
266
+ },
267
+ {
268
+ key: "email_verified_at",
269
+ label: "Email Verified",
270
+ condition: {
271
+ table: false,
272
+ form: false,
273
+ },
274
+ render: {
275
+ read: ({ model }) =>
276
+ h(
277
+ "span",
278
+ {},
279
+ model.email_verified_at
280
+ ? new Date(model.email_verified_at).toLocaleString()
281
+ : "Not verified",
282
+ ),
283
+ },
284
+ },
285
+ {
286
+ key: "verified",
287
+ label: "Verified",
288
+ condition: {
289
+ description: false,
290
+ form: false,
291
+ },
292
+ render: {
293
+ read: ({ model }) =>
294
+ h(
295
+ "span",
296
+ {
297
+ title: model.email_verified_at
298
+ ? `Verified: ${new Date(model.email_verified_at).toLocaleString()}`
299
+ : "Not verified",
300
+ style: {
301
+ color: model.email_verified_at ? "green" : "red",
302
+ fontSize: "18px",
303
+ },
304
+ },
305
+ model.email_verified_at ? "✓" : "✗",
306
+ ),
307
+ },
308
+ },
309
+ {
310
+ key: "roles",
311
+ label: "Role",
312
+ condition: {
313
+ description: false,
314
+ form: {
315
+ write: ({ $store }) => $store.getters.can("edit roles"),
316
+ },
317
+ },
318
+ initial: ({ model }) =>
319
+ model?.overrides_roles_and_permissions
320
+ ? model?.roles.map((val) => ({
321
+ label: val.label,
322
+ value: val.id,
323
+ }))
324
+ : [],
325
+ preparation: ({ form }) =>
326
+ form.roles.map((d) => (typeof d === "object" ? d.value : d)),
327
+ render: {
328
+ read: ({ model }) =>
329
+ h(
330
+ !model.overrides_roles_and_permissions ||
331
+ model.roles.length === 1
332
+ ? Chip
333
+ : Chips,
334
+ !model.overrides_roles_and_permissions
335
+ ? {
336
+ name: "inherited",
337
+ label: "Inherited",
338
+ }
339
+ : model.roles.length === 1
340
+ ? {
341
+ name: model.roles[0].name,
342
+ label: model.roles[0].label,
343
+ }
344
+ : { array: model.roles },
345
+ ),
346
+ write: ({ model, form }) => h(RolesField, { form }),
347
+ },
348
+ },
349
+ ];