@eeplatform/nuxt-layer-common 1.7.47 → 1.7.48

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @eeplatform/nuxt-layer-common
2
2
 
3
+ ## 1.7.48
4
+
5
+ ### Patch Changes
6
+
7
+ - 47a0840: Revise role management
8
+
3
9
  ## 1.7.47
4
10
 
5
11
  ### Patch Changes
@@ -79,7 +79,7 @@
79
79
 
80
80
  <v-row no-gutters class="px-2 pb-2">
81
81
  <v-col
82
- v-for="item in props.apps"
82
+ v-for="item in apps"
83
83
  :key="item.title"
84
84
  cols="4"
85
85
  class="pa-2"
@@ -206,7 +206,7 @@ const props = defineProps({
206
206
  default: "Title",
207
207
  },
208
208
  defaults: {
209
- type: Array as PropType<Array<TApp>>,
209
+ type: Array as PropType<Record<string, any>[]>,
210
210
  default: () => [
211
211
  {
212
212
  title: "Account",
@@ -223,11 +223,11 @@ const props = defineProps({
223
223
  ],
224
224
  },
225
225
  apps: {
226
- type: Array as PropType<Array<TApp>>,
226
+ type: Array as PropType<Record<string, any>[]>,
227
227
  default: () => [],
228
228
  },
229
229
  others: {
230
- type: Array as PropType<Array<TApp>>,
230
+ type: Array as PropType<Record<string, any>[]>,
231
231
  default: () => [],
232
232
  },
233
233
  });
@@ -244,7 +244,7 @@ function setSearchValue() {
244
244
  search.value = _search.value;
245
245
  }
246
246
 
247
- const { redirect, drawer } = useLocal();
247
+ const { redirect, drawer, apps } = useLocal();
248
248
 
249
249
  const { APP_NAME, APP_NAME_ROUTE } = useRuntimeConfig().public;
250
250
 
@@ -0,0 +1,134 @@
1
+ <template>
2
+ <v-card width="100%" v-bind="attrs">
3
+ <v-list
4
+ v-model:selected="selected"
5
+ lines="two"
6
+ :select-strategy="attrs.readonly ? 'classic' : 'leaf'"
7
+ class="pa-0"
8
+ density="compact"
9
+ read-only
10
+ open-strategy="single"
11
+ >
12
+ <template name="prepend"></template>
13
+
14
+ <template
15
+ v-for="(keyGroup, keyGroupIndex) in props.items"
16
+ :key="keyGroupIndex"
17
+ >
18
+ <v-divider v-if="keyGroupIndex > 0"></v-divider>
19
+ <v-list-group :value="keyGroup.title" fluid>
20
+ <template v-slot:activator="{ props }">
21
+ <v-list-item v-bind="props" density="compact">
22
+ <span class="text-capitalize">
23
+ {{ keyGroup.title }}
24
+ </span>
25
+
26
+ <template #prepend>
27
+ <v-chip class="mr-2" small>
28
+ {{ selectedActionCount(String(keyGroup.key)) }}
29
+ </v-chip>
30
+ </template>
31
+ </v-list-item>
32
+ </template>
33
+
34
+ <template
35
+ v-for="(item, itemIndex) in keyGroup.children"
36
+ :key="itemIndex"
37
+ >
38
+ <v-divider></v-divider>
39
+ <v-list-item v-if="attrs.readonly" density="compact">
40
+ <template #title>
41
+ <span class="text-subtitle-2 text-capitalize pl-11">
42
+ {{ item.title }}
43
+ </span>
44
+ </template>
45
+
46
+ <template #subtitle>
47
+ <span class="text-subtitle-2 pl-11">{{
48
+ item.description
49
+ }}</span>
50
+ </template>
51
+ </v-list-item>
52
+
53
+ <v-list-item v-else :value="item.key" density="compact">
54
+ <template #title>
55
+ <span class="text-subtitle-2 text-capitalize">
56
+ {{ item.title }}
57
+ </span>
58
+ </template>
59
+
60
+ <template #subtitle>
61
+ <span class="text-subtitle-2 text-capitalize">
62
+ {{ item.description }}
63
+ </span>
64
+ </template>
65
+
66
+ <template #prepend="{ isSelected }">
67
+ <v-list-item-action start class="pl-1">
68
+ <v-checkbox-btn
69
+ :model-value="isSelected"
70
+ readonly
71
+ @click="addItem(item.key)"
72
+ ></v-checkbox-btn>
73
+ </v-list-item-action>
74
+ </template>
75
+ </v-list-item>
76
+ </template>
77
+ </v-list-group>
78
+ </template>
79
+
80
+ <slot v-if="noData" name="no-data">
81
+ <v-list-item>
82
+ <v-list-item-title>No data</v-list-item-title>
83
+ </v-list-item>
84
+ </slot>
85
+
86
+ <template name="append"></template>
87
+ </v-list>
88
+ </v-card>
89
+ </template>
90
+
91
+ <script setup lang="ts">
92
+ const selected = defineModel<Array<string>>({ default: [] });
93
+ const attrs = useAttrs();
94
+
95
+ function addItem(key: string) {
96
+ if (selected.value.includes(key)) {
97
+ // remove item from selected without reassigning the array
98
+ const index = selected.value.indexOf(key);
99
+ if (index > -1) {
100
+ selected.value.splice(index, 1);
101
+ }
102
+ } else {
103
+ selected.value.push(key);
104
+ }
105
+ }
106
+
107
+ type TListGroup = {
108
+ title: string;
109
+ key: string;
110
+ children: { title: string; key: string; description: string }[];
111
+ };
112
+
113
+ const props = defineProps({
114
+ items: {
115
+ // Array of object
116
+ type: Array as PropType<TListGroup[]>,
117
+ required: true,
118
+ default: () => [],
119
+ },
120
+ message: {
121
+ type: String,
122
+ default: "",
123
+ },
124
+ });
125
+
126
+ const selectedActionCount = (resource: string) => {
127
+ return selected.value.filter((permission) => permission.startsWith(resource))
128
+ .length;
129
+ };
130
+
131
+ const noData = computed(() => {
132
+ return Object.keys(props.items).length === 0;
133
+ });
134
+ </script>
@@ -0,0 +1,327 @@
1
+ <template>
2
+ <v-card width="100%">
3
+ <v-toolbar>
4
+ <v-row no-gutters class="fill-height px-6" align="center">
5
+ <span class="font-weight-bold text-h5 text-capitalize">
6
+ {{ localProps.title }}
7
+ </span>
8
+ </v-row>
9
+ </v-toolbar>
10
+
11
+ <v-card-text style="max-height: 100vh; overflow-y: auto">
12
+ <v-form v-model="valid">
13
+ <v-row no-gutters>
14
+ <v-col v-if="isOrg" cols="12" class="mb-1">
15
+ <v-row no-gutters>
16
+ <InputLabel
17
+ class="text-capitalize"
18
+ title="App"
19
+ :required="isMutable"
20
+ />
21
+ <v-col cols="12">
22
+ <v-select
23
+ v-model="role.app"
24
+ :items="apps"
25
+ item-title="name"
26
+ item-value="code"
27
+ :readonly="!isMutable"
28
+ :loading="loadingApps"
29
+ ></v-select>
30
+ </v-col>
31
+ </v-row>
32
+ </v-col>
33
+
34
+ <v-col cols="12" class="mb-1">
35
+ <v-row no-gutters>
36
+ <InputLabel
37
+ class="text-capitalize"
38
+ title="Name"
39
+ :required="isMutable"
40
+ />
41
+ <v-col cols="12">
42
+ <v-text-field
43
+ v-model="role.name"
44
+ :rules="isMutable ? [requiredRule] : []"
45
+ :readonly="!isMutable"
46
+ ></v-text-field>
47
+ </v-col>
48
+ </v-row>
49
+ </v-col>
50
+
51
+ <v-col cols="12" class="mb-1">
52
+ <v-row no-gutters>
53
+ <InputLabel
54
+ class="text-capitalize"
55
+ title="Permissions"
56
+ :required="isMutable"
57
+ />
58
+ <v-col cols="12">
59
+ <ListGroupSelection
60
+ v-model="role.permissions"
61
+ :items="permissions"
62
+ :readonly="!isMutable"
63
+ variant="outlined"
64
+ border="thin"
65
+ :loading="loadingPermissions"
66
+ />
67
+ <v-input
68
+ :messages="errorPermissionSelection"
69
+ :error="!!errorPermissionSelection"
70
+ ></v-input>
71
+ </v-col>
72
+ </v-row>
73
+ </v-col>
74
+
75
+ <v-col cols="12" class="mb-1">
76
+ <v-row no-gutters>
77
+ <InputLabel class="text-capitalize" title="Description" />
78
+ <v-col cols="12">
79
+ <v-textarea
80
+ v-model="role.description"
81
+ rows="2"
82
+ no-resize
83
+ :readonly="!isMutable"
84
+ ></v-textarea>
85
+ </v-col>
86
+ </v-row>
87
+ </v-col>
88
+ </v-row>
89
+ </v-form>
90
+
91
+ <v-alert
92
+ v-if="message"
93
+ type="error"
94
+ variant="flat"
95
+ closable
96
+ position="absolute"
97
+ location="bottom"
98
+ style="bottom: 48px"
99
+ @click:close="message = ''"
100
+ width="100%"
101
+ tile
102
+ class="text-caption"
103
+ >
104
+ {{ message }}
105
+ </v-alert>
106
+ </v-card-text>
107
+
108
+ <v-toolbar density="compact">
109
+ <v-row no-gutters>
110
+ <v-col v-if="isMutable" cols="6">
111
+ <v-btn
112
+ tile
113
+ block
114
+ variant="text"
115
+ class="text-none"
116
+ size="48"
117
+ @click="emits('cancel')"
118
+ >
119
+ Cancel
120
+ </v-btn>
121
+ </v-col>
122
+
123
+ <v-col v-if="localProps.mode === 'view'" cols="6">
124
+ <v-btn
125
+ tile
126
+ block
127
+ variant="text"
128
+ class="text-none"
129
+ size="48"
130
+ @click="emits('close')"
131
+ >
132
+ Close
133
+ </v-btn>
134
+ </v-col>
135
+
136
+ <v-col v-if="localProps.mode === 'view'" cols="6">
137
+ <v-menu>
138
+ <template #activator="{ props }">
139
+ <v-btn
140
+ block
141
+ variant="flat"
142
+ color="black"
143
+ class="text-none"
144
+ height="48"
145
+ v-bind="props"
146
+ tile
147
+ >
148
+ More actions
149
+ </v-btn>
150
+ </template>
151
+
152
+ <v-list class="pa-0">
153
+ <v-list-item @click="emits('edit')">
154
+ <v-list-item-title class="text-subtitle-2">
155
+ Edit Role
156
+ </v-list-item-title>
157
+ </v-list-item>
158
+
159
+ <v-list-item @click="emits('delete')" class="text-red">
160
+ <v-list-item-title class="text-subtitle-2">
161
+ Delete Role
162
+ </v-list-item-title>
163
+ </v-list-item>
164
+ </v-list>
165
+ </v-menu>
166
+ </v-col>
167
+
168
+ <v-col v-if="isMutable" cols="6">
169
+ <v-btn
170
+ tile
171
+ block
172
+ variant="flat"
173
+ color="black"
174
+ class="text-none"
175
+ size="48"
176
+ @click="emits('submit')"
177
+ :disabled="!valid"
178
+ >
179
+ {{ localProps.mode === "add" ? "Add Role" : "Save Changes" }}
180
+ </v-btn>
181
+ </v-col>
182
+ </v-row>
183
+ </v-toolbar>
184
+ </v-card>
185
+ </template>
186
+
187
+ <script setup lang="ts">
188
+ const emits = defineEmits(["submit", "cancel", "close", "edit", "delete"]);
189
+
190
+ const isMutable = computed(() => ["add", "edit"].includes(localProps.mode));
191
+
192
+ const localProps = defineProps({
193
+ title: {
194
+ type: String,
195
+ default: "Role Form",
196
+ },
197
+ app: {
198
+ type: String,
199
+ default: "",
200
+ },
201
+ mode: {
202
+ type: String,
203
+ default: "add",
204
+ },
205
+ });
206
+
207
+ const role = defineModel<TRole>({
208
+ default: () => useRole().role.value,
209
+ });
210
+
211
+ const errorPermissionSelection = computed(() =>
212
+ requireListRule(role.value.permissions ?? [])
213
+ );
214
+
215
+ const message = defineModel("message", { default: "" });
216
+
217
+ const valid = ref(false);
218
+
219
+ const { requiredRule, requireListRule } = useUtils();
220
+
221
+ const apps = ref<TApp[]>([]);
222
+
223
+ const { getAll: getAllApps } = useApps();
224
+
225
+ const isOrg = computed(() => localProps.app === "org");
226
+
227
+ const {
228
+ data: appsData,
229
+ status: appsReqStatus,
230
+ refresh: refreshApps,
231
+ } = await useLazyAsyncData(
232
+ `role-form-get-apps-${localProps.app}`,
233
+ () =>
234
+ getAllApps({
235
+ limit: 20,
236
+ }),
237
+ {
238
+ immediate: isOrg.value,
239
+ }
240
+ );
241
+
242
+ const loadingApps = computed(() => appsReqStatus.value === "pending");
243
+
244
+ watchEffect(() => {
245
+ if (appsData.value) {
246
+ apps.value = appsData.value.items;
247
+ }
248
+ });
249
+
250
+ type PermChild = {
251
+ title: string;
252
+ key: string;
253
+ description: string;
254
+ };
255
+
256
+ type PermGroup = {
257
+ title: string;
258
+ key: string;
259
+ children: PermChild[];
260
+ };
261
+
262
+ const permissions = ref<PermGroup[]>([]);
263
+
264
+ const { getAllPerm } = usePermission();
265
+
266
+ const {
267
+ data: permissionsData,
268
+ status: permissionsReqStatus,
269
+ refresh: refreshPermissions,
270
+ } = await useLazyAsyncData(
271
+ `role-form-get-all-permission-${role.value.app}`,
272
+ () =>
273
+ getAllPerm({
274
+ app: role.value.app,
275
+ limit: 200,
276
+ }),
277
+ {
278
+ watch: [() => role.value.app],
279
+ }
280
+ );
281
+
282
+ const loadingPermissions = computed(
283
+ () => permissionsReqStatus.value === "pending"
284
+ );
285
+
286
+ function groupPermissions(permissions: TPerm[]): PermGroup[] {
287
+ const groups: Record<string, PermGroup> = {};
288
+
289
+ for (const perm of permissions) {
290
+ if (perm.status !== "active") continue;
291
+
292
+ const groupKey = perm.group;
293
+
294
+ if (!groups[groupKey]) {
295
+ groups[groupKey] = {
296
+ title: groupKey,
297
+ key: groupKey,
298
+ children: [],
299
+ };
300
+ }
301
+
302
+ groups[groupKey].children.push({
303
+ title: perm.name,
304
+ key: perm.key,
305
+ description: perm.description,
306
+ });
307
+ }
308
+
309
+ return Object.values(groups);
310
+ }
311
+
312
+ watchEffect(() => {
313
+ if (
314
+ permissionsData.value &&
315
+ permissionsData.value &&
316
+ permissionsData.value.items
317
+ ) {
318
+ let items = permissionsData.value.items;
319
+ if (localProps.mode === "view") {
320
+ items = permissionsData.value.items.filter((p: TPerm) =>
321
+ role.value.permissions?.includes(p.key)
322
+ );
323
+ }
324
+ permissions.value = groupPermissions(items);
325
+ }
326
+ });
327
+ </script>
@@ -6,11 +6,11 @@
6
6
  class="text-none"
7
7
  rounded="pill"
8
8
  variant="tonal"
9
- @click="createDialog = true"
9
+ @click="setRole({ mode: 'add', dialog: true })"
10
10
  size="large"
11
11
  v-if="canCreateRole"
12
12
  >
13
- Create role
13
+ Add Role
14
14
  </v-btn>
15
15
  </v-row>
16
16
  </v-col>
@@ -60,98 +60,66 @@
60
60
  </span>
61
61
  <v-chip>{{ value.length }}</v-chip>
62
62
  </template>
63
-
64
- <template #item.action-table="{ item }" v-if="canDeleteRole">
65
- <v-menu :close-on-content-click="false" offset-y width="150">
66
- <template v-slot:activator="{ props }">
67
- <v-icon v-bind="props">mdi-dots-horizontal</v-icon>
68
- </template>
69
- <v-list>
70
- <v-list-item @click="openDeleteDialog(item._id)">
71
- Delete Role
72
- </v-list-item>
73
- </v-list>
74
- </v-menu>
75
- </template>
76
63
  </v-data-table>
77
64
  </v-card>
78
65
  </v-col>
79
66
 
80
67
  <!-- dialogs -->
81
- <v-dialog v-model="createDialog" width="500" persistent>
82
- <RolePermissionFormCreate
83
- @cancel="createDialog = false"
84
- :permissions="props.permissions"
85
- :id="props.id"
86
- @success="success()"
87
- v-model:type="type"
88
- :types="props.types"
89
- @success:create-more="getRoles()"
68
+ <v-dialog v-model="dialogAdd" width="500" persistent>
69
+ <RoleForm
70
+ v-model="role"
71
+ v-model:message="message"
72
+ :app="props.app"
73
+ @cancel="setRole({ mode: 'add' })"
74
+ @submit="submitAdd()"
90
75
  />
91
76
  </v-dialog>
92
77
 
93
- <v-dialog v-model="previewDialog" width="500" persistent>
94
- <RolePermissionFormPreviewUpdate
95
- @cancel="previewDialog = false"
96
- @success="successfulUpdate()"
97
- :permissions="Permissions"
98
- :original-permissions="originalPermissions"
99
- v-model="selectedPermissions"
100
- v-model:edit="edit"
101
- :name="name"
102
- :id="roleId"
78
+ <v-dialog v-model="dialogPreview" width="500" persistent>
79
+ <RoleForm
80
+ title="Role Details"
81
+ :app="props.app"
82
+ v-model="role"
83
+ @close="setRole({ mode: 'view' })"
84
+ @edit="handOpenEdit()"
85
+ @delete="handleOpenDelete()"
86
+ mode="view"
103
87
  />
104
88
  </v-dialog>
105
89
 
106
- <ConfirmDialog
107
- v-model="confirmDialog"
108
- :loading="deleteLoading"
109
- @submit="handleDeleteRole"
110
- >
111
- <template #title>
112
- <span class="font-weight-medium text-h5">Delete Role</span>
113
- </template>
114
-
115
- <template #description>
116
- <p class="text-subtitle-2">
117
- Are you sure you want to delete this role? This action cannot be
118
- undone.
119
- </p>
120
- </template>
121
-
122
- <template #footer>
123
- <v-btn
124
- variant="text"
125
- @click="confirmDialog = false"
126
- :disabled="deleteLoading"
127
- >
128
- Close
129
- </v-btn>
130
- <v-btn
131
- color="primary"
132
- variant="flat"
133
- @click="handleDeleteRole"
134
- :loading="deleteLoading"
135
- >
136
- Delete Role
137
- </v-btn>
138
- </template>
139
- </ConfirmDialog>
90
+ <v-dialog v-model="dialogEdit" width="500" persistent>
91
+ <RoleForm
92
+ title="Edit Role Details"
93
+ :app="props.app"
94
+ v-model="role"
95
+ @cancel="setRole({ mode: 'edit' })"
96
+ @submit="submitEdit()"
97
+ mode="edit"
98
+ />
99
+ </v-dialog>
140
100
 
141
- <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
101
+ <v-dialog v-model="dialogDelete" width="450" persistent>
102
+ <ConfirmationPrompt
103
+ title="Delete Role"
104
+ action="Delete Role"
105
+ content="Are you sure you want to delete this role? This action cannot be undone."
106
+ @cancel="setRole({ mode: 'delete' })"
107
+ @confirm="submitDelete()"
108
+ v-model:message="message"
109
+ />
110
+ </v-dialog>
142
111
  </v-row>
143
112
  </template>
144
113
 
145
114
  <script setup lang="ts">
146
115
  const props = defineProps({
147
- id: {
116
+ app: {
148
117
  type: String,
149
118
  default: "",
150
119
  },
151
- permissions: {
152
- type: Object,
153
- required: true,
154
- default: () => ({}),
120
+ org: {
121
+ type: String,
122
+ default: "",
155
123
  },
156
124
  types: {
157
125
  type: Array as PropType<Array<{ title: string; value: string }>>,
@@ -168,7 +136,7 @@ const props = defineProps({
168
136
  title: "permissions",
169
137
  value: "permissions",
170
138
  },
171
- { title: "Action", value: "action-table" },
139
+ { title: "App", value: "app" },
172
140
  ],
173
141
  },
174
142
  canCreateRole: {
@@ -193,40 +161,75 @@ const props = defineProps({
193
161
  },
194
162
  });
195
163
 
196
- const type = defineModel<string>("type", {
197
- type: String,
198
- default: "",
199
- });
200
-
201
164
  const { headerSearch } = useLocal();
202
165
 
203
- const { getRoles: _getRoles } = useRole();
166
+ const {
167
+ role,
168
+ add: addRole,
169
+ getAll: _getRoles,
170
+ updateById,
171
+ deleteById,
172
+ } = useRole();
204
173
 
205
174
  const page = ref(1);
206
175
  const pages = ref(0);
207
176
  const pageRange = ref("-- - -- of --");
177
+ const modeRole = ref("");
208
178
 
209
179
  const message = ref("");
210
- const messageSnackbar = ref(false);
211
- const messageColor = ref("");
180
+
181
+ function setRole({
182
+ mode = "",
183
+ dialog = false,
184
+ data = useRole().role.value,
185
+ } = {}) {
186
+ Object.assign(
187
+ role.value,
188
+ JSON.parse(
189
+ JSON.stringify({
190
+ ...data,
191
+ app: mode === "admin" || mode === "add" ? props.app : data.app,
192
+ })
193
+ )
194
+ );
195
+ modeRole.value = mode;
196
+ message.value = "";
197
+
198
+ if (modeRole.value === "add") {
199
+ dialogAdd.value = dialog;
200
+ }
201
+
202
+ if (modeRole.value === "view") {
203
+ dialogPreview.value = dialog;
204
+ }
205
+
206
+ if (modeRole.value === "edit") {
207
+ dialogEdit.value = dialog;
208
+ }
209
+
210
+ if (modeRole.value === "delete") {
211
+ dialogDelete.value = dialog;
212
+ }
213
+ }
212
214
 
213
215
  const items = ref<Array<Record<string, any>>>([]);
214
216
 
217
+ const isOrg = computed(() => props.app === "org");
218
+
215
219
  const {
216
220
  data: getRoleReq,
217
221
  refresh: getRoles,
218
222
  status: getRoleReqStatus,
219
223
  } = useLazyAsyncData(
220
- "roles-permissions-get-all" + "-" + props.id,
224
+ `roles-permissions-get-all-by-${props.app}-${props.org}-page-${page.value}`,
221
225
  () =>
222
226
  _getRoles({
223
227
  page: page.value,
224
- search: headerSearch.value,
225
- type: type.value,
226
- id: props.id,
228
+ org: props.org,
229
+ app: isOrg.value ? "" : props.app,
227
230
  }),
228
231
  {
229
- watch: [page, headerSearch],
232
+ watch: [page],
230
233
  }
231
234
  );
232
235
 
@@ -241,135 +244,64 @@ watchEffect(() => {
241
244
  });
242
245
 
243
246
  function tableRowClickHandler(_: any, data: any) {
244
- previewDialog.value = true;
245
- roleId.value = data.item._id;
247
+ setRole({ data: data.item, mode: "view", dialog: true });
246
248
  }
247
249
 
248
- const createDialog = ref(false);
249
-
250
- function success() {
251
- createDialog.value = false;
252
- type.value = "";
253
- getRoles();
254
- }
255
-
256
- const previewDialog = ref(false);
257
-
258
- const name = ref("");
259
- const selectedPermissions = ref<Array<string>>([]);
260
- const originalPermissions = ref<Array<string>>([]);
261
- const edit = ref(false);
250
+ const dialogAdd = ref(false);
262
251
 
263
- watchEffect(() => {
264
- if (!edit.value) {
265
- selectedPermissions.value = originalPermissions.value;
266
- }
267
- });
252
+ const dialogPreview = ref(false);
268
253
 
269
- const roleId = ref("");
254
+ const { loggedInUser } = useLocalAuth();
270
255
 
271
- const { getRoleById: _getRoleById, deleteRole } = useRole();
272
-
273
- const { data: role, refresh: getRoleById } = useLazyAsyncData(
274
- "role-permissions-get-by-id",
275
- () => _getRoleById(roleId.value)
276
- );
277
-
278
- watchEffect(() => {
279
- if (roleId.value) {
280
- getRoleById();
281
- }
282
- });
283
-
284
- watchEffect(() => {
285
- if (role.value) {
286
- name.value = role.value.name as any;
287
-
288
- selectedPermissions.value = role.value.permissions as any;
289
- originalPermissions.value = role.value.permissions as any;
256
+ async function submitAdd() {
257
+ try {
258
+ await addRole({
259
+ name: role.value.name,
260
+ permissions: role.value.permissions,
261
+ app: role.value.app,
262
+ org: props.org,
263
+ createdBy: loggedInUser(),
264
+ });
265
+ setRole({ mode: "add" });
266
+ getRoles();
267
+ } catch (error: any) {
268
+ message.value = error.response._data.message;
290
269
  }
291
- });
292
-
293
- function filterPermissions(
294
- allPermissions: TPermissions,
295
- storedPermissions: string[]
296
- ) {
297
- const filteredPermissions: TPermissions = {};
298
-
299
- Object.entries(allPermissions).forEach(([resource, actions]) => {
300
- const filteredActions = Object.entries(actions)
301
- .filter(([action]) => storedPermissions.includes(`${resource}:${action}`))
302
- .reduce((acc: Record<string, any>, [action, data]) => {
303
- acc[action] = data;
304
- return acc;
305
- }, {});
306
-
307
- if (Object.keys(filteredActions).length > 0) {
308
- filteredPermissions[resource] = filteredActions;
309
- }
310
- });
311
-
312
- return filteredPermissions;
313
270
  }
314
271
 
315
- const { permissions: orgPermissions } = useOrgPermission();
316
- const { permissions: schoolPermissions } = useSchoolPermission();
317
-
318
- const permissions = computed(() => {
319
- if (role.value?.type === "organization") {
320
- return orgPermissions;
321
- }
322
-
323
- if (role.value?.type === "school") {
324
- return schoolPermissions;
325
- }
326
-
327
- return {};
328
- });
329
-
330
- const Permissions = computed(() => {
331
- return edit.value
332
- ? permissions.value
333
- : filterPermissions(permissions.value, selectedPermissions.value);
334
- });
272
+ const dialogEdit = ref(false);
273
+ const dialogDelete = ref(false);
335
274
 
336
- function successfulUpdate() {
337
- previewDialog.value = false;
338
- getRoles();
339
- getRoleById();
340
- edit.value = false;
275
+ function handOpenEdit() {
276
+ dialogPreview.value = false;
277
+ dialogEdit.value = true;
341
278
  }
342
279
 
343
- const confirmDialog = ref(false);
344
- const selectedRoleId = ref<string | null>(null);
345
- const deleteLoading = ref(false);
346
-
347
- function openDeleteDialog(id: string) {
348
- selectedRoleId.value = id;
349
- confirmDialog.value = true;
280
+ async function submitEdit() {
281
+ try {
282
+ await updateById(role.value._id ?? "", {
283
+ name: role.value.name,
284
+ permissions: role.value.permissions,
285
+ });
286
+ await setRole({ mode: "edit" });
287
+ await getRoles();
288
+ } catch (error: any) {
289
+ message.value = error.response._data.message;
290
+ }
350
291
  }
351
292
 
352
- function showMessage(msg: string, color: string) {
353
- message.value = msg;
354
- messageColor.value = color;
355
- messageSnackbar.value = true;
293
+ function handleOpenDelete() {
294
+ dialogPreview.value = false;
295
+ dialogDelete.value = true;
356
296
  }
357
297
 
358
- async function handleDeleteRole() {
359
- if (!selectedRoleId.value) return;
360
- deleteLoading.value = true;
298
+ async function submitDelete() {
361
299
  try {
362
- const res = await deleteRole(selectedRoleId.value);
363
-
364
- confirmDialog.value = false;
365
- showMessage(res.message, "success");
300
+ await deleteById(role.value._id ?? "");
301
+ dialogDelete.value = false;
366
302
  getRoles();
367
303
  } catch (error: any) {
368
- const errorMessage = error?.response?._data?.message;
369
- showMessage(errorMessage, "error");
370
- } finally {
371
- deleteLoading.value = false;
372
- selectedRoleId.value = null;
304
+ message.value = error.response._data.message;
373
305
  }
374
306
  }
375
307
  </script>
@@ -0,0 +1,58 @@
1
+ export default function useApps() {
2
+ const app = ref<TApp>({
3
+ _id: "",
4
+ code: "",
5
+ name: "",
6
+ description: "",
7
+ status: "active",
8
+ createdAt: "",
9
+ updatedAt: "",
10
+ deletedAt: "",
11
+ });
12
+
13
+ const resource = "/api/apps";
14
+
15
+ function add(value: TApp) {
16
+ return useNuxtApp().$api(resource, {
17
+ method: "POST",
18
+ body: value,
19
+ });
20
+ }
21
+
22
+ function getAll({
23
+ page = 1,
24
+ limit = 10,
25
+ search = "",
26
+ status = "active",
27
+ type = "standard",
28
+ } = {}) {
29
+ return useNuxtApp().$api<Record<string, any>>(resource, {
30
+ method: "GET",
31
+ query: { page, limit, search, status, type },
32
+ });
33
+ }
34
+
35
+ function updateById(
36
+ id: string,
37
+ value: { name: string; description: string }
38
+ ) {
39
+ return useNuxtApp().$api(`${resource}/id/${id}`, {
40
+ method: "PATCH",
41
+ body: value,
42
+ });
43
+ }
44
+
45
+ function deleteById(id: string) {
46
+ return useNuxtApp().$api(`${resource}/id/${id}`, {
47
+ method: "DELETE",
48
+ });
49
+ }
50
+
51
+ return {
52
+ app,
53
+ add,
54
+ getAll,
55
+ updateById,
56
+ deleteById,
57
+ };
58
+ }
@@ -12,7 +12,7 @@ export default function useLocal() {
12
12
  const { APP_STUDENT, APP_PARENT, APP_SCHOOL, APP_DIVISION, APP_REGION } =
13
13
  appConfig;
14
14
 
15
- const apps = computed<Array<TApp>>(() => {
15
+ const apps = computed(() => {
16
16
  return [
17
17
  {
18
18
  title: "Student",
@@ -2,6 +2,18 @@ export default function useLocalAuth() {
2
2
  const { cookieConfig } = useRuntimeConfig().public;
3
3
 
4
4
  const currentUser = useState<TUser | null>("currentUser", () => null);
5
+ const permissions = useState<string[]>("permissions", () => []);
6
+
7
+ const hasPermission = (permissionKey: string): ComputedRef<boolean> => {
8
+ return computed(() => {
9
+ if (!permissions.value.length) return false;
10
+ return permissions.value.some((perm) => perm === permissionKey);
11
+ });
12
+ };
13
+
14
+ function loggedInUser() {
15
+ return useCookie("user", cookieConfig).value ?? "";
16
+ }
5
17
 
6
18
  function authenticate() {
7
19
  if (currentUser.value) {
@@ -128,6 +140,9 @@ export default function useLocalAuth() {
128
140
  }
129
141
 
130
142
  return {
143
+ loggedInUser,
144
+ permissions,
145
+ hasPermission,
131
146
  authenticate,
132
147
  login,
133
148
  setSession,
@@ -47,8 +47,105 @@ export default function usePermission() {
47
47
  return false;
48
48
  }
49
49
 
50
+ const perm = ref<TPerm>({
51
+ app: "",
52
+ key: "",
53
+ name: "",
54
+ group: "",
55
+ description: "",
56
+ });
57
+
58
+ function addPerm(value: TPerm) {
59
+ return useNuxtApp().$api("/api/permissions", {
60
+ method: "POST",
61
+ body: value,
62
+ });
63
+ }
64
+
65
+ function updatePermById(
66
+ id: string,
67
+ value: Pick<TPerm, "key" | "name" | "group" | "description">
68
+ ) {
69
+ return useNuxtApp().$api(`/api/permissions/id/${id}`, {
70
+ method: "PATCH",
71
+ body: value,
72
+ });
73
+ }
74
+
75
+ function getAllPerm({
76
+ page = 1,
77
+ limit = 10,
78
+ search = "",
79
+ app = "",
80
+ status = "active",
81
+ } = {}) {
82
+ return useNuxtApp().$api<Record<string, any>>("/api/permissions", {
83
+ method: "GET",
84
+ query: { page, limit, search, app, status },
85
+ });
86
+ }
87
+
88
+ function deletePermById(id: string) {
89
+ return useNuxtApp().$api(`/api/permissions/id/${id}`, {
90
+ method: "DELETE",
91
+ });
92
+ }
93
+
94
+ const permGroup = ref<TPermGroup>({
95
+ app: "",
96
+ key: "",
97
+ label: "",
98
+ order: 0,
99
+ });
100
+
101
+ function addPermGroup(value: Pick<TPermGroup, "app" | "key" | "label">) {
102
+ return useNuxtApp().$api("/api/permissions/groups", {
103
+ method: "POST",
104
+ body: value,
105
+ });
106
+ }
107
+
108
+ function updatePermGroupById(
109
+ id: string,
110
+ value: Pick<TPermGroup, "key" | "label">
111
+ ) {
112
+ return useNuxtApp().$api(`/api/permissions/groups/id/${id}`, {
113
+ method: "PATCH",
114
+ body: value,
115
+ });
116
+ }
117
+
118
+ function getAllPermGroup({
119
+ page = 1,
120
+ limit = 10,
121
+ search = "",
122
+ app = "",
123
+ status = "active",
124
+ } = {}) {
125
+ return useNuxtApp().$api<Record<string, any>>("/api/permissions/groups", {
126
+ method: "GET",
127
+ query: { page, limit, search, app, status },
128
+ });
129
+ }
130
+
131
+ function deletePermGroupById(id: string) {
132
+ return useNuxtApp().$api(`/api/permissions/groups/id/${id}`, {
133
+ method: "DELETE",
134
+ });
135
+ }
136
+
50
137
  return {
51
138
  listPermissions,
52
139
  hasPermission,
140
+ perm,
141
+ addPerm,
142
+ updatePermById,
143
+ getAllPerm,
144
+ deletePermById,
145
+ permGroup,
146
+ addPermGroup,
147
+ updatePermGroupById,
148
+ getAllPermGroup,
149
+ deletePermGroupById,
53
150
  };
54
151
  }
@@ -1,44 +1,36 @@
1
1
  export default function useRole() {
2
- function createRole(
3
- { name, permissions, type } = {} as {
4
- name: string;
5
- permissions: Array<string>;
6
- type: string;
7
- }
2
+ function add(
3
+ value: Pick<TRole, "name" | "permissions" | "app" | "org" | "createdBy">
8
4
  ) {
9
5
  return useNuxtApp().$api("/api/roles", {
10
6
  method: "POST",
11
- body: { name, permissions, type },
7
+ body: value,
12
8
  });
13
9
  }
14
10
 
15
- function getRoles({
11
+ function getAll({
16
12
  search = "",
17
13
  page = 1,
18
14
  limit = 20,
19
- type = "",
20
- id = "",
15
+ app = "",
16
+ org = "",
21
17
  } = {}) {
22
18
  return useNuxtApp().$api<Record<string, any>>("/api/roles", {
23
19
  method: "GET",
24
- query: { search, page, limit, type, id },
20
+ query: { search, page, limit, app, org },
25
21
  });
26
22
  }
27
23
 
28
- function getRoleById(id: string) {
29
- return useNuxtApp().$api<TRole>(`/api/roles/id/${id}`, {
24
+ function getById(id: string) {
25
+ return useNuxtApp().$api<Record<string, any>>(`/api/roles/id/${id}`, {
30
26
  method: "GET",
31
27
  });
32
28
  }
33
29
 
34
- function updateRoleById(
35
- _id: string,
36
- name?: string,
37
- permissions?: Array<string>
38
- ) {
39
- return useNuxtApp().$api(`/api/roles/id/${_id}`, {
30
+ function updateById(id: string, value: Pick<TRole, "name" | "permissions">) {
31
+ return useNuxtApp().$api(`/api/roles/id/${id}`, {
40
32
  method: "PATCH",
41
- body: { name, permissions },
33
+ body: value,
42
34
  });
43
35
  }
44
36
 
@@ -56,33 +48,28 @@ export default function useRole() {
56
48
  });
57
49
  }
58
50
 
59
- function deleteRole(_id: string) {
51
+ function deleteById(_id: string) {
60
52
  return useNuxtApp().$api<Record<string, any>>(`/api/roles/${_id}`, {
61
53
  method: "DELETE",
62
54
  });
63
55
  }
64
56
 
65
- const role = useState("userRole", (): TRole => {
66
- return {
67
- _id: "",
68
- name: "",
69
- id: "",
70
- permissions: [],
71
- createdAt: "",
72
- updatedAt: "",
73
- deletedAt: "",
74
- default: false,
75
- };
57
+ const role = ref<TRole>({
58
+ name: "",
59
+ org: "",
60
+ permissions: [],
61
+ app: "",
62
+ description: "",
76
63
  });
77
64
 
78
65
  return {
79
66
  role,
80
- createRole,
81
- getRoles,
82
- getRoleById,
83
- updateRoleById,
67
+ add,
68
+ getAll,
69
+ getById,
70
+ updateById,
84
71
  updateRoleFieldById,
85
- deleteRole,
72
+ deleteById,
86
73
  updatePermissionById,
87
74
  };
88
75
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@eeplatform/nuxt-layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "1.7.47",
5
+ "version": "1.7.48",
6
6
  "main": "./nuxt.config.ts",
7
7
  "publishConfig": {
8
8
  "access": "public"
package/types/local.d.ts CHANGED
@@ -19,8 +19,12 @@ declare type TNavigationItem = {
19
19
  };
20
20
 
21
21
  declare type TApp = {
22
- title: string;
23
- icon: string;
24
- link: string;
25
- landingPage?: string;
22
+ _id?: string;
23
+ code: string;
24
+ name: string;
25
+ description: string;
26
+ status?: string;
27
+ createdAt?: string | Date;
28
+ updatedAt?: string | Date;
29
+ deletedAt?: string | Date;
26
30
  };
@@ -12,3 +12,27 @@ declare type TPermissions = {
12
12
  [action: string]: TPermission;
13
13
  };
14
14
  };
15
+
16
+ declare type TPerm = {
17
+ _id?: string;
18
+ app: string;
19
+ name: string;
20
+ key: string;
21
+ group: string;
22
+ description: string;
23
+ status?: string;
24
+ createdAt?: string | Date;
25
+ updatedAt?: string | Date;
26
+ deletedAt?: string | Date;
27
+ };
28
+
29
+ declare type TPermGroup = {
30
+ _id?: string;
31
+ app: string;
32
+ key: string;
33
+ label: string;
34
+ order: number;
35
+ createdAt?: string | Date;
36
+ updatedAt?: string | Date;
37
+ deletedAt?: string | Date;
38
+ };
package/types/role.d.ts CHANGED
@@ -2,8 +2,9 @@ declare type TRole = {
2
2
  _id?: string;
3
3
  name?: string;
4
4
  permissions?: Array<string>;
5
- type?: string;
6
- id?: string;
5
+ app: string;
6
+ org?: string;
7
+ description?: string;
7
8
  status?: string;
8
9
  default?: boolean;
9
10
  createdBy?: string;