@7365admin1/layer-common 3.2.8-staging.216 → 3.2.8-staging.218

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.
@@ -84,8 +84,16 @@
84
84
  {{ formatDateInvited(item.dateInvited) }}
85
85
  </template>
86
86
  <template #item.action-table="{ item }">
87
+ <!--
88
+ `props.assignRole` belongs in this test. The menu's only item that
89
+ is not status-dependent is Assign Role, but the menu itself opened
90
+ only for suspend / activate / delete -- so a role granted
91
+ `members:assign-member-role` and nothing else got no menu at all,
92
+ and the permission was unreachable.
93
+ -->
87
94
  <v-menu
88
95
  v-if="
96
+ props.assignRole ||
89
97
  (currentStatus === 'active' &&
90
98
  (canSuspendMembers || canDeleteMembers)) ||
91
99
  (currentStatus === 'suspended' &&
@@ -317,7 +317,12 @@ async function submit() {
317
317
  });
318
318
  emit("success");
319
319
  } catch (error: any) {
320
- message.value = error.response._data.message;
320
+ // Same unguarded chain as the preview dialog had: on a network or timeout
321
+ // failure `error.response` is undefined and the catch threw inside itself,
322
+ // leaving the user with no message at all.
323
+ message.value =
324
+ error?.response?._data?.message ??
325
+ "Could not create this role. Please check your connection and try again.";
321
326
  } finally {
322
327
  disable.value = false;
323
328
  }
@@ -99,15 +99,12 @@
99
99
  </div>
100
100
  </v-col>
101
101
 
102
- <v-col v-if="edit" cols="12" class="my-2">
103
- <v-row no-gutters>
104
- <v-col cols="12" class="text-center">
105
- <span class="role-perm__error">{{ message }}</span>
106
- </v-col>
107
- </v-row>
108
- </v-col>
109
-
110
- <v-col cols="12" class="my-2">
102
+ <!--
103
+ One message, once. This block used to appear twice -- inside
104
+ `v-if="edit"` and again unconditionally -- so a refused save printed
105
+ the server's sentence back to back.
106
+ -->
107
+ <v-col v-if="message" cols="12" class="my-2">
111
108
  <v-row no-gutters>
112
109
  <v-col cols="12" class="text-center">
113
110
  <span class="role-perm__error">{{ message }}</span>
@@ -122,7 +119,7 @@
122
119
  <AppButton v-if="!edit" variant="ghost" @click="emit('cancel')">Close</AppButton>
123
120
  <AppButton v-else variant="ghost" @click="edit = false">Cancel</AppButton>
124
121
 
125
- <AppButton v-if="!edit" :disabled="disable" @click="edit = true">Edit</AppButton>
122
+ <AppButton v-if="!edit && canUpdateRole" :disabled="disable" @click="edit = true">Edit</AppButton>
126
123
  <AppButton v-else :disabled="Boolean(errorMessages) || disable" @click="submit()">
127
124
  Submit
128
125
  </AppButton>
@@ -159,6 +156,15 @@ const props = defineProps({
159
156
  type: Array as PropType<string[]>,
160
157
  default: () => [],
161
158
  },
159
+ // "Update role". This dialog had no permission prop at all, so it showed Edit
160
+ // to everyone -- including on the seeded platform-owner role -- and the
161
+ // `canUpdateRole` the apps have been passing to `RolePermissionMain` in good
162
+ // faith did nothing. `RolePermissionMain` is the only thing that renders this
163
+ // dialog and always passes it, so the default is closed.
164
+ canUpdateRole: {
165
+ type: Boolean,
166
+ default: false,
167
+ },
162
168
  });
163
169
 
164
170
  const validForm = ref(false);
@@ -282,7 +288,12 @@ async function submit() {
282
288
  await updatePermissionById(props.id, definedModel.value);
283
289
  emit("success");
284
290
  } catch (error: any) {
285
- message.value = error.response._data.message;
291
+ // `error.response` is undefined on a network or timeout failure, so the old
292
+ // unguarded chain threw inside its own catch: nothing was shown, the button
293
+ // came back enabled, and the save looked like it had simply been ignored.
294
+ message.value =
295
+ error?.response?._data?.message ??
296
+ "Could not save this role. Please check your connection and try again.";
286
297
  } finally {
287
298
  disable.value = false;
288
299
  }
@@ -42,12 +42,13 @@
42
42
 
43
43
  <v-data-table
44
44
  :headers="props.headers"
45
- :items="items"
45
+ :items="visibleItems"
46
46
  item-value="_id"
47
47
  items-per-page="20"
48
48
  fixed-header
49
49
  hide-default-footer
50
50
  hide-default-header
51
+ :no-data-text="emptyText"
51
52
  @click:row="tableRowClickHandler"
52
53
  style="max-height: calc(100vh - (180px))"
53
54
  >
@@ -102,6 +103,7 @@
102
103
  v-model:edit="edit"
103
104
  :name="name"
104
105
  :id="roleId"
106
+ :can-update-role="props.canUpdateRole"
105
107
  :web-only-resources="props.webOnlyResources"
106
108
  />
107
109
  </v-dialog>
@@ -245,13 +247,18 @@ const props = defineProps({
245
247
  type: Boolean,
246
248
  default: false,
247
249
  },
250
+ // "See all roles". Defaults to `true` because nine of the ten pages that
251
+ // render this component do not pass it, and until now nothing read it at all
252
+ // -- so `true` is exactly what those pages have always had. The one page that
253
+ // does pass it now gets a gate that works.
248
254
  canViewRole: {
249
255
  type: Boolean,
250
- default: false,
256
+ default: true,
251
257
  },
258
+ // "See role details" -- opening a role. Same reasoning as `canViewRole`.
252
259
  canViewByRole: {
253
260
  type: Boolean,
254
- default: false,
261
+ default: true,
255
262
  },
256
263
  canDeleteRole: {
257
264
  type: Boolean,
@@ -281,6 +288,7 @@ const {
281
288
  data: getRoleReq,
282
289
  refresh: getRoles,
283
290
  status: getRoleReqStatus,
291
+ error: getRoleReqError,
284
292
  } = useLazyAsyncData(
285
293
  "roles-permissions-get-all",
286
294
  () =>
@@ -305,7 +313,33 @@ watchEffect(() => {
305
313
  }
306
314
  });
307
315
 
316
+ // "See all roles". Without it the list is not drawn at all, and `emptyText`
317
+ // below says so rather than leaving a blank table to be read as a failure.
318
+ const visibleItems = computed(() => (props.canViewRole ? items.value : []));
319
+
320
+ /**
321
+ * SAY WHY THE TABLE IS EMPTY. "No data available" was shown for three
322
+ * different situations -- you may not see roles, the request failed, and this
323
+ * organisation genuinely has none -- so a refusal was indistinguishable from an
324
+ * empty list and neither was distinguishable from a broken screen.
325
+ */
326
+ const emptyText = computed(() => {
327
+ if (!props.canViewRole) return "You do not have permission to view roles.";
328
+ const status = (getRoleReqError.value as any)?.response?.status
329
+ ?? (getRoleReqError.value as any)?.statusCode;
330
+ if (status === 401 || status === 403) {
331
+ return "You do not have permission to view the roles for this organisation.";
332
+ }
333
+ if (getRoleReqError.value) {
334
+ return "Could not load roles. Please refresh to try again.";
335
+ }
336
+ return "No roles have been created yet.";
337
+ });
338
+
308
339
  function tableRowClickHandler(_: any, data: any) {
340
+ // "See role details". Ungated until now, which is why granting or withholding
341
+ // it changed nothing.
342
+ if (!props.canViewByRole) return;
309
343
  previewDialog.value = true;
310
344
  roleId.value = data.item._id;
311
345
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "3.2.8-staging.216",
5
+ "version": "3.2.8-staging.218",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",