@fishawack/lab-velocity 2.0.0-beta.5 → 2.0.0-beta.50

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 (111) hide show
  1. package/README.md +467 -36
  2. package/_Build/js/libs/build-id.js +14 -0
  3. package/_Build/js/libs/filters.js +36 -0
  4. package/_Build/js/libs/globals.js +7 -0
  5. package/_Build/js/libs/router.js +22 -0
  6. package/_Build/js/libs/routes.js +29 -0
  7. package/_Build/js/libs/store.js +21 -0
  8. package/_Build/js/libs/utility.js +161 -0
  9. package/_Build/vue/components/basic/Button.vue +1 -1
  10. package/_Build/vue/components/form/Avatar.vue +90 -0
  11. package/_Build/vue/components/form/Checkbox.vue +10 -0
  12. package/_Build/vue/components/form/InputNumber.vue +1 -1
  13. package/_Build/vue/components/form/Select.vue +223 -33
  14. package/_Build/vue/components/form/Spinner.vue +5 -0
  15. package/_Build/vue/components/layout/Alert.vue +5 -5
  16. package/_Build/vue/components/layout/Audit.vue +143 -0
  17. package/_Build/vue/{modules/AuthModule/components/VBreadcrumbs.vue → components/layout/Breadcrumbs.vue} +4 -4
  18. package/_Build/vue/{modules/AuthModule/components → components/layout}/Chips.vue +2 -2
  19. package/_Build/vue/components/layout/Footer.vue +11 -10
  20. package/_Build/vue/{modules/AuthModule/components/VFormFooter.vue → components/layout/FormFooter.vue} +13 -7
  21. package/_Build/vue/{modules/AuthModule/components → components/layout}/FormRole.vue +10 -8
  22. package/_Build/vue/components/layout/Layout.vue +94 -0
  23. package/_Build/vue/components/layout/Navigation.vue +77 -0
  24. package/_Build/vue/{modules/AuthModule/components/VPageHeader.vue → components/layout/PageHeader.vue} +14 -8
  25. package/_Build/vue/components/layout/SideBar.vue +26 -0
  26. package/_Build/vue/{modules/AuthModule/components/VTable.vue → components/layout/Table.vue} +37 -16
  27. package/_Build/vue/{modules/AuthModule/components/VTableSorter.vue → components/layout/TableSorter.vue} +108 -52
  28. package/_Build/vue/components/layout/TokenDisplay.vue +52 -0
  29. package/_Build/vue/components/layout/pageTitle.vue +1 -1
  30. package/_Build/vue/components/navigation/MenuItem.vue +7 -2
  31. package/_Build/vue/components/navigation/MenuItemGroup.vue +7 -2
  32. package/_Build/vue/modules/AuthModule/js/axios.js +21 -1
  33. package/_Build/vue/modules/AuthModule/js/guest-request.js +32 -0
  34. package/_Build/vue/modules/AuthModule/js/impersonation-banner.js +102 -0
  35. package/_Build/vue/modules/AuthModule/js/router.js +91 -114
  36. package/_Build/vue/modules/AuthModule/js/store.js +23 -6
  37. package/_Build/vue/modules/AuthModule/routes/PCompanies/columns.js +268 -0
  38. package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +213 -0
  39. package/_Build/vue/modules/AuthModule/routes/PIntegrations/columns.js +58 -0
  40. package/_Build/vue/modules/AuthModule/routes/PIntegrations/resource.js +79 -0
  41. package/_Build/vue/modules/AuthModule/routes/PTeams/columns.js +78 -0
  42. package/_Build/vue/modules/AuthModule/routes/PTeams/resource.js +251 -0
  43. package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordAction.vue +51 -0
  44. package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordDialog.vue +138 -0
  45. package/_Build/vue/modules/AuthModule/routes/PUsers/columns.js +349 -0
  46. package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +239 -0
  47. package/_Build/vue/modules/AuthModule/routes/account-exists.vue +2 -2
  48. package/_Build/vue/modules/AuthModule/routes/change-password.vue +28 -32
  49. package/_Build/vue/modules/AuthModule/routes/container.vue +2 -11
  50. package/_Build/vue/modules/AuthModule/routes/expired-reset.vue +4 -4
  51. package/_Build/vue/modules/AuthModule/routes/expired-verification.vue +10 -9
  52. package/_Build/vue/modules/AuthModule/routes/force-reset.vue +44 -58
  53. package/_Build/vue/modules/AuthModule/routes/forgot.vue +10 -5
  54. package/_Build/vue/modules/AuthModule/routes/login.vue +12 -19
  55. package/_Build/vue/modules/AuthModule/routes/logincallback.vue +1 -3
  56. package/_Build/vue/modules/AuthModule/routes/loginsso.vue +14 -10
  57. package/_Build/vue/modules/AuthModule/routes/logout.vue +17 -5
  58. package/_Build/vue/modules/AuthModule/routes/logoutheadless.vue +1 -3
  59. package/_Build/vue/modules/AuthModule/routes/register.vue +24 -28
  60. package/_Build/vue/modules/AuthModule/routes/reset.vue +20 -14
  61. package/_Build/vue/modules/AuthModule/routes/success-forgot.vue +14 -8
  62. package/_Build/vue/modules/AuthModule/routes/success-reset.vue +2 -2
  63. package/_Build/vue/modules/AuthModule/routes/success-verify.vue +1 -3
  64. package/_Build/vue/modules/AuthModule/routes/verify.vue +11 -14
  65. package/_Build/vue/modules/resource/Children/create.vue +81 -0
  66. package/_Build/vue/modules/resource/Children/edit.vue +106 -0
  67. package/_Build/vue/modules/resource/Children/index.vue +42 -0
  68. package/_Build/vue/modules/resource/Children/partials/form.vue +111 -0
  69. package/_Build/vue/modules/resource/Children/show.vue +166 -0
  70. package/_Build/vue/modules/resource/index.js +561 -0
  71. package/_Build/vue/modules/resource/parent.vue +63 -0
  72. package/_Build/vue/modules/resource/trashable.js +104 -0
  73. package/_base.scss +0 -1
  74. package/_defaults.scss +2 -13
  75. package/_variables.scss +9 -4
  76. package/{modules/_AuthModule.scss → components/_auth.scss} +19 -68
  77. package/components/_datepicker.scss +1 -0
  78. package/components/_descriptions.scss +2 -0
  79. package/components/_footer.scss +1 -0
  80. package/components/_form.scss +18 -0
  81. package/components/_header.scss +3 -27
  82. package/components/_layout.scss +56 -0
  83. package/components/_menu.scss +0 -5
  84. package/components/_sidebar.scss +12 -27
  85. package/components/_table.scss +3 -0
  86. package/components/_token-display.scss +41 -0
  87. package/general.scss +1 -0
  88. package/index.js +31 -1
  89. package/package.json +7 -4
  90. package/vendor.scss +0 -1
  91. package/_Build/vue/components/layout/sideBar.vue +0 -25
  92. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/Upload/upload.vue +0 -251
  93. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/create.vue +0 -62
  94. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/edit.vue +0 -98
  95. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/index.vue +0 -90
  96. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/partials/form.vue +0 -173
  97. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/show.vue +0 -262
  98. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/parent.vue +0 -36
  99. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/create.vue +0 -112
  100. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/edit.vue +0 -103
  101. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/index.vue +0 -112
  102. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/partials/form.vue +0 -169
  103. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/show.vue +0 -120
  104. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/parent.vue +0 -36
  105. package/components/_input.scss +0 -0
  106. package/modules/_AuthVariables.scss +0 -7
  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
  111. /package/{modules → components}/_modal.scss +0 -0
@@ -3,8 +3,22 @@
3
3
  import { h } from "vue";
4
4
  import { RouterView } from "vue-router";
5
5
 
6
+ import { routes as resourceRoutes } from "../../resource/index.js";
7
+
8
+ import defaultUserResource from "../routes/PUsers/resource.js";
9
+ import defaultCompanyResource from "../routes/PCompanies/resource.js";
10
+ import defaultTeamResource from "../routes/PTeams/resource.js";
11
+ import defaultIntegrationResource from "../routes/PIntegrations/resource.js";
12
+
6
13
  // Admin routes export - minimal auth flow (headless login only)
7
- export function adminRoutes(node) {
14
+ export function adminRoutes(node, overrides = {}) {
15
+ const {
16
+ userResource = defaultUserResource,
17
+ companyResource = defaultCompanyResource,
18
+ teamResource = defaultTeamResource,
19
+ integrationResource = defaultIntegrationResource,
20
+ } = overrides;
21
+
8
22
  return [
9
23
  {
10
24
  path: "/auth",
@@ -37,96 +51,25 @@ export function adminRoutes(node) {
37
51
  : require("../routes/logincallback.vue").default,
38
52
  name: "auth.callback",
39
53
  },
40
- ],
41
- },
42
- {
43
- path: "/users",
44
- component: node
45
- ? ""
46
- : require("../adminRoutes/PUsers/parent.vue").default,
47
- children: [
48
54
  {
49
- path: "",
50
- component: node
51
- ? ""
52
- : require("../adminRoutes/PUsers/Children/index.vue")
53
- .default,
54
- name: "users.index",
55
- },
56
- {
57
- path: "create",
58
- component: node
59
- ? ""
60
- : require("../adminRoutes/PUsers/Children/create.vue")
61
- .default,
62
- name: "users.create",
63
- },
64
- {
65
- path: ":id",
66
- component: node
67
- ? ""
68
- : require("../adminRoutes/PUsers/Children/show.vue")
69
- .default,
70
- name: "users.show",
71
- },
72
- {
73
- path: ":id/edit",
74
- component: node
75
- ? ""
76
- : require("../adminRoutes/PUsers/Children/edit.vue")
77
- .default,
78
- name: "users.edit",
79
- },
80
- ],
81
- },
82
- {
83
- path: "/companies",
84
- component: node
85
- ? ""
86
- : require("../adminRoutes/PCompanies/parent.vue").default,
87
- children: [
88
- {
89
- path: "",
90
- component: node
91
- ? ""
92
- : require("../adminRoutes/PCompanies/Children/index.vue")
93
- .default,
94
- name: "companies.index",
95
- },
96
- {
97
- path: "create",
98
- component: node
99
- ? ""
100
- : require("../adminRoutes/PCompanies/Children/create.vue")
101
- .default,
102
- name: "companies.create",
103
- },
104
- {
105
- path: ":id",
106
- component: node
107
- ? ""
108
- : require("../adminRoutes/PCompanies/Children/show.vue")
109
- .default,
110
- name: "companies.show",
111
- },
112
- {
113
- path: ":id/edit",
114
- component: node
115
- ? ""
116
- : require("../adminRoutes/PCompanies/Children/edit.vue")
117
- .default,
118
- name: "companies.edit",
119
- },
120
- {
121
- path: ":id/upload",
122
- component: node
123
- ? ""
124
- : require("../adminRoutes/PCompanies/Children/Upload/upload.vue")
125
- .default,
126
- name: "companies.upload",
55
+ path: "verify",
56
+ component: require("../routes/verify.vue").default,
57
+ name: "auth.verify",
127
58
  },
128
59
  ],
129
60
  },
61
+ ...resourceRoutes(node, ...userResource),
62
+ ...resourceRoutes(node, ...companyResource, [
63
+ ...resourceRoutes(node, teamResource[0], {
64
+ ...teamResource[1],
65
+ routeName: "companies.teams",
66
+ }),
67
+ ]),
68
+ ...resourceRoutes(node, teamResource[0], {
69
+ ...teamResource[1],
70
+ routeName: "teams",
71
+ }),
72
+ ...resourceRoutes(node, ...integrationResource),
130
73
  ];
131
74
  }
132
75
 
@@ -241,50 +184,84 @@ export function routes(node) {
241
184
  }
242
185
 
243
186
  export function beforeEach(router, store) {
187
+ let initialLoad = true;
188
+
189
+ // These routes must always be reachable regardless of auth/verification state.
190
+ // Without this, force_password_change or unverified state can trap the user with
191
+ // no way to log out or complete an SSO callback.
192
+ const ALWAYS_ALLOW = new Set(["auth.logout", "auth.callback"]);
193
+
244
194
  router.beforeEach(async (to, from, next) => {
245
- // If authenticated query param is present, assume authentication has happened elsewhere and attempt to fetch user data
246
- if (to.query.authenticated && !store.getters.authenticated) {
195
+ // Refresh user state on initial page load (handles cross-app state sync)
196
+ // or when authenticated query param is present (bypass/redirect flow)
197
+ if (
198
+ (store.getters.authenticated && initialLoad) ||
199
+ to.query.authenticated
200
+ ) {
247
201
  await store.dispatch("getUser", {
248
202
  errors: (e) => console.error(e),
249
203
  });
250
204
  }
251
205
 
206
+ initialLoad = false;
207
+
252
208
  const { user, redirect } = store.state.auth;
209
+ const isGuestRoute = to.matched.some((d) => d.meta.guest) === true;
253
210
 
254
- // User verification handling and redirect if user alrady verified but accessing an expired verification route
211
+ // Logout and SSO callback must always be reachable prevents every
212
+ // possible "stuck" scenario caused by state-based redirects below.
213
+ if (ALWAYS_ALLOW.has(to.name)) {
214
+ return next();
215
+ }
216
+
217
+ // Redirect already-verified users away from verification routes
255
218
  if (
256
219
  to.query.verified ||
257
220
  (to.name === "auth.expired-verification" && user?.email_verified_at)
258
221
  ) {
259
- next({ name: "auth.success-verify" });
260
- } else if (store.getters.authenticated) {
261
- // User is authenticated - check permissions and redirect appropriately
262
- if (user.force_password_change && to.name !== "auth.force-reset") {
263
- // User needs to change password - redirect to force reset
264
- next({ name: "auth.force-reset" });
265
- } else if (to.name === "auth.login") {
266
- // Already logged in user hitting login
267
- next({ name: redirect });
268
- } else if (
222
+ return next({ name: "auth.success-verify" });
223
+ }
224
+
225
+ if (store.getters.authenticated) {
226
+ // 1. Email verification is the highest priority redirect.
227
+ // Must come before force_password_change — there is no point forcing
228
+ // a password change on an account that hasn't been verified yet.
229
+ // Guest routes, auth.verify and auth.expired-verification are exempt
230
+ // so the user can always complete or re-request verification.
231
+ if (
269
232
  !user?.email_verified_at &&
270
- to.matched.some((d) => d.meta.guest) !== true
233
+ !isGuestRoute &&
234
+ to.name !== "auth.verify" &&
235
+ to.name !== "auth.expired-verification"
271
236
  ) {
272
- // User needs email verification and trying to access protected route
273
- next({ name: "auth.verify" });
274
- } else {
275
- // User is authenticated and authorized - proceed
276
- next();
237
+ return next({ name: "auth.verify" });
277
238
  }
278
- } else {
279
- // User is not authenticated - handle guest routes and login redirects
280
- if (to.matched.some((d) => d.meta.guest) === true) {
281
- // Route allows guest access - proceed
282
- next();
283
- } else {
284
- // Protected route requires authentication - redirect to standard login
285
- next({ name: "auth.login" });
239
+
240
+ // 2. Force password change applies only after verification is satisfied.
241
+ // Guest routes are exempt so the user can still navigate within /auth
242
+ // (e.g. auth.verify, auth.expired-verification) without looping.
243
+ if (
244
+ user?.force_password_change &&
245
+ !isGuestRoute &&
246
+ to.name !== "auth.force-reset"
247
+ ) {
248
+ return next({ name: "auth.force-reset" });
249
+ }
250
+
251
+ // 3. Redirect an already-authenticated user away from the login page
252
+ if (to.name === "auth.login") {
253
+ return next({ name: redirect });
286
254
  }
255
+
256
+ return next();
257
+ }
258
+
259
+ // Unauthenticated: allow guest routes, otherwise redirect to login
260
+ if (isGuestRoute) {
261
+ return next();
287
262
  }
263
+
264
+ return next({ name: "auth.login" });
288
265
  });
289
266
  }
290
267
 
@@ -7,6 +7,9 @@ const store = {
7
7
  intended: null,
8
8
  user: null,
9
9
  redirect: process.env.HYDRATE_REDIRECT ?? "index",
10
+ logo: process.env.HYDRATE_LOGO ?? "example-logo",
11
+ logoReverse:
12
+ process.env.HYDRATE_LOGO_REVERSE ?? process.env.HYDRATE_LOGO,
10
13
  contact:
11
14
  process.env.HYDRATE_CONTACT ?? "mailto:det@avalerehealth.com",
12
15
  };
@@ -15,12 +18,18 @@ const store = {
15
18
  getters: {
16
19
  authenticated: (state) => !!state.user,
17
20
  can: (state) => (permission) => {
18
- return state.user?.permissions
19
- .map(({ name }) => name)
20
- .includes(permission);
21
+ const arr = Array.isArray(permission) ? permission : [permission];
22
+ const userPermissions = state.user?.permissions.map(
23
+ ({ name }) => name,
24
+ );
25
+
26
+ return arr.every((name) => userPermissions?.includes(name));
21
27
  },
22
28
  hasRole: (state) => (role) => {
23
- return state.user?.roles.map(({ name }) => name).includes(role);
29
+ const arr = Array.isArray(role) ? role : [role];
30
+ const userRoles = state.user?.roles.map(({ name }) => name);
31
+
32
+ return arr.every((name) => userRoles?.includes(name));
24
33
  },
25
34
  },
26
35
 
@@ -38,7 +47,7 @@ const store = {
38
47
  },
39
48
 
40
49
  actions: {
41
- getUser({ commit }, { errors, query = "", params }) {
50
+ getUser({ commit }, { errors = console.log, query = "", params } = {}) {
42
51
  return axios
43
52
  .get(`/api/users/self${query}`, {
44
53
  params,
@@ -51,11 +60,19 @@ const store = {
51
60
  .catch(errors);
52
61
  },
53
62
 
54
- logout({ commit }, { errors }) {
63
+ logout({ commit }) {
55
64
  commit("setUser", null);
56
65
 
57
66
  return axios.post("/logout");
58
67
  },
68
+
69
+ stopImpersonating({ commit }) {
70
+ return axios.post("/api/users/impersonate/stop").then((res) => {
71
+ commit("setUser", res.data.data);
72
+
73
+ return res.data.data;
74
+ });
75
+ },
59
76
  },
60
77
  };
61
78
 
@@ -0,0 +1,268 @@
1
+ import { h, resolveComponent } from "vue";
2
+ import { ElInput } from "element-plus";
3
+
4
+ import VelBasic from "../../../../components/form/basic.vue";
5
+ import VelButton from "../../../../components/basic/Button.vue";
6
+ import VelSelect from "../../../../components/form/Select.vue";
7
+ import VelFormRole from "../../../../components/layout/FormRole.vue";
8
+ import Chip from "../../../../components/layout/Chip.vue";
9
+ import Chips from "../../../../components/layout/Chips.vue";
10
+
11
+ export default [
12
+ {
13
+ key: "name",
14
+ sortable: true,
15
+ },
16
+ {
17
+ key: "primary_contact",
18
+ label: "Primary Contact",
19
+ condition: {
20
+ table: false,
21
+ form: {
22
+ write: ({ method }) => method === "patch",
23
+ },
24
+ },
25
+ preparation: ({ form }) => form.primary_contact?.id,
26
+ render: {
27
+ read: ({ model }) => h("span", model.primary_contact?.name),
28
+ write: ({ $route }) =>
29
+ h(VelSelect, {
30
+ endpoint: `api/users`,
31
+ params: {
32
+ "filter[company_id]": $route.params.companiesId,
33
+ },
34
+ labelKey: "name",
35
+ filterable: true,
36
+ searchParam: "name",
37
+ }),
38
+ },
39
+ },
40
+ {
41
+ key: "primary_contact_email",
42
+ label: "Primary Contact Email",
43
+ condition: {
44
+ table: false,
45
+ form: false,
46
+ },
47
+ render: {
48
+ read: ({ model }) => h("span", model.primary_contact?.email),
49
+ },
50
+ },
51
+ {
52
+ key: "primary_contact_contacted",
53
+ label: "Primary Contact Contacted",
54
+ condition: {
55
+ table: false,
56
+ form: false,
57
+ },
58
+ render: {
59
+ read: ({ model }) =>
60
+ h("span", String(!!model.primary_contact_contacted)),
61
+ },
62
+ },
63
+ {
64
+ key: "domains",
65
+ condition: {
66
+ table: false,
67
+ },
68
+ initial: ({ model }) => model?.domains || [],
69
+ render: {
70
+ read: ({ model }) => h("span", model.domains.join(", ")),
71
+ write: ({ form }) =>
72
+ h("div", { class: "form__group vel-basic" }, [
73
+ h("p", { class: "my-0" }, [
74
+ "Domain/s ",
75
+ h("sup", { class: "color-status-red-100" }, "*"),
76
+ ]),
77
+ h(
78
+ "ul",
79
+ { class: "list-none pl-0 mt mb-2" },
80
+ form.domains.map((domain, index) =>
81
+ h("li", { key: index, class: "mb-0.5" }, [
82
+ h(
83
+ ElInput,
84
+ {
85
+ modelValue: form.domains[index],
86
+ "onUpdate:modelValue": (val) => {
87
+ form.domains[index] = val;
88
+ },
89
+ placeholder: "avalerehealth.com",
90
+ },
91
+ {
92
+ prepend: () => "@",
93
+ append: () =>
94
+ h(
95
+ VelButton,
96
+ {
97
+ type: "danger",
98
+ onClick: () =>
99
+ form.domains.splice(
100
+ index,
101
+ 1,
102
+ ),
103
+ },
104
+ () =>
105
+ h(
106
+ resolveComponent(
107
+ "GSvg",
108
+ ),
109
+ {
110
+ class: "vel-icon",
111
+ name: "icon-trash",
112
+ },
113
+ ),
114
+ ),
115
+ },
116
+ ),
117
+ form.errors?.has?.(`domains.${index}`) &&
118
+ h(
119
+ "small",
120
+ {
121
+ class: "form__error vel-basic__error",
122
+ },
123
+ form.errors.first(`domains.${index}`),
124
+ ),
125
+ ]),
126
+ ),
127
+ ),
128
+ h("div", [
129
+ h(
130
+ VelButton,
131
+ {
132
+ onClick: () => form.domains.push(""),
133
+ },
134
+ () => [
135
+ "Add Domain ",
136
+ h(resolveComponent("GIcon"), {
137
+ name: "icon-plus",
138
+ embed: true,
139
+ asis: true,
140
+ class: "fill-0 icon--0.5 ml",
141
+ }),
142
+ ],
143
+ ),
144
+ ]),
145
+ ]),
146
+ },
147
+ },
148
+ {
149
+ key: "seats",
150
+ label: "Available Seats",
151
+ type: "number",
152
+ min: 0,
153
+ condition: {
154
+ table: false,
155
+ },
156
+ },
157
+ {
158
+ key: "user_count",
159
+ label: "Total Users",
160
+ width: "150",
161
+ condition: {
162
+ form: false,
163
+ },
164
+ },
165
+ {
166
+ key: "sso_enabled",
167
+ label: "SSO Enabled",
168
+ condition: {
169
+ table: false,
170
+ form: false,
171
+ },
172
+ },
173
+ {
174
+ key: "roles",
175
+ label: "Role",
176
+ condition: {
177
+ description: false,
178
+ form: {
179
+ write: ({ $store }) => $store.getters.can("edit roles"),
180
+ },
181
+ },
182
+ initial: ({ model }) =>
183
+ model?.roles.map((val) => ({
184
+ label: val.label,
185
+ value: val.id,
186
+ })) || [],
187
+ preparation: ({ form }) =>
188
+ form.roles.map((d) => (typeof d === "object" ? d.value : d)),
189
+ render: {
190
+ read: ({ model }) =>
191
+ h(
192
+ model.roles.length === 1 ? Chip : Chips,
193
+ model.roles.length === 1
194
+ ? {
195
+ name: model.roles[0].name,
196
+ label: model.roles[0].label,
197
+ }
198
+ : { array: model.roles },
199
+ ),
200
+ write: ({ model, form }) =>
201
+ h(VelFormRole, {
202
+ overrides: model?.overrides_roles_and_permissions,
203
+ form,
204
+ }),
205
+ },
206
+ },
207
+ {
208
+ key: "sso_type",
209
+ label: "Provider",
210
+ condition: {
211
+ table: false,
212
+ description: false,
213
+ form: {
214
+ write: ({ $store }) => $store.getters.can("edit sso"),
215
+ },
216
+ },
217
+ initial: ({ model }) => model?.sso_type || undefined,
218
+ preparation: ({ form }) => form.sso_type?.value,
219
+ render: {
220
+ write: () =>
221
+ h(VelSelect, {
222
+ clearable: true,
223
+ class: "mt-2",
224
+ options: [
225
+ { value: "azure", label: "azure" },
226
+ { value: "google", label: "google" },
227
+ ],
228
+ }),
229
+ },
230
+ },
231
+ {
232
+ key: "sso_client_id",
233
+ label: "Key",
234
+ condition: {
235
+ table: false,
236
+ description: false,
237
+ form: {
238
+ write: ({ $store }) => $store.getters.can("edit sso"),
239
+ },
240
+ },
241
+ initial: ({ model }) => model?.sso_client_id || undefined,
242
+ },
243
+ {
244
+ key: "sso_tenant",
245
+ label: "Tenant",
246
+ condition: {
247
+ table: false,
248
+ description: false,
249
+ form: {
250
+ write: ({ $store }) => $store.getters.can("edit sso"),
251
+ },
252
+ },
253
+ initial: ({ model }) => model?.sso_tenant || undefined,
254
+ },
255
+ {
256
+ key: "sso_client_secret",
257
+ label: "Secret",
258
+ type: "password",
259
+ condition: {
260
+ table: false,
261
+ description: false,
262
+ form: {
263
+ write: ({ $store }) => $store.getters.can("edit sso"),
264
+ },
265
+ },
266
+ initial: ({ model }) => model?.sso_client_secret || undefined,
267
+ },
268
+ ];